对于 Android 开发工程师来说, Toast 在熟悉不过了, 用它来显示一个提示信息, 并自动隐藏在我们开发 RN 应用的时候, 我门也要实现这样的效果, 就一点困难了, 倒也不是困难, 只是需要我们去适配, RN 官方提供了一个 API ToastAndroid, 看到这个名字应该猜出, 它只能在 Android 中使用, 在 iOS 中使用没有效果, 所以, 我们需要适配或者我们自定义一个, 今天的这篇文章就是自定义一个 Toast 使其在 Android 和 iOS 都能运行, 并有相同的运行效果
源码传送门 https://github.com/xiehui999/helloReactNative/tree/master/component/com/Toast
定义组件
- import React, {Component} from 'react';
- import {
- StyleSheet,
- View,
- Easing,
- Dimensions,
- Text,
- Animated
- } from 'react-native';
- import PropTypes from 'prop-types';
- import Toast from "./index";
- const {width, height} = Dimensions.get("window");
- const viewHeight = 35;
- class ToastView extends Component {
- static propTypes = {
- message:PropTypes.string,
- };
- dismissHandler = null;
- constructor(props) {
- super(props);
- this.state = {
- message: props.message !== undefined ? props.message : ''
- }
- }
- render() {
- return (
- <View style={styles.container} pointerEvents='none'>
- <Animated.View style={[styles.textContainer]}><Text
- style={styles.defaultText}>{this.state.message}</Text></Animated.View>
- </View>
- )
- }
- componentDidMount() {
- this.timingDismiss()
- }
- componentWillUnmount() {
- clearTimeout(this.dismissHandler)
- }
- timingDismiss = () => {
- this.dismissHandler = setTimeout(() => {
- this.onDismiss()
- }, 1000)
- };
- onDismiss = () => {
- if (this.props.onDismiss) {
- this.props.onDismiss()
- }
- }
- }
- const styles = StyleSheet.create({
- textContainer: {
- backgroundColor: 'rgba(0,0,0,.6)',
- borderRadius: 8,
- padding: 10,
- bottom:height/8,
- maxWidth: width / 2,
- alignSelf: "flex-end",
- },
- defaultText: {
- color: "#FFF",
- fontSize: 15,
- },
- container: {
- position: "absolute",
- left: 0,
- right: 0,
- top: 0,
- bottom: 0,
- flexDirection: "row",
- justifyContent: "center",
- }
- });
- export default ToastView
首先导入我们必须的基础组件以及 API, 我们自定义组件都需要继承它, Dimensions 用于获取屏幕尺寸, Easing 用于设置动画的轨迹运行效果, PropTypes 用于对属性类型进行定义
render 方法是我们定义组件渲染的入口, 最外层 view 使用 position 为 absolute, 并设置 left,right,top,bottom 设置为 0, 使其占满屏幕, 这样使用 Toast 显示期间不让界面监听点击事件内层 View 是 Toast 显示的黑框容器, backgroundColor 属性设置 rgba 形式, 颜色为黑色透明度为 0.6 并设置圆角以及最大宽度为屏幕宽度的一半然后就是 Text 组件用于显示具体的提示信息
我们还看到 propTypes 用于限定属性 message 的类型为 stringconstructor 是我们组件的构造方法, 有一个 props 参数, 此参数为传递过来的一些属性需要注意, 构造方法中首先要调用 super(props), 否则报错, 在此处, 我将传递来的值设置到了 state 中
对于 Toast, 显示一会儿自动消失, 我们可以通过 setTimeout 实现这个效果, 在 componentDidMount 调用此方法, 此处设置时间为 1000ms 然后将隐藏毁掉暴露出去当我们使用 setTimeout 时还需要在组件卸载时清除定时器组件卸载时回调的时 componentWillUnmount 所以在此处清除定时器
- # 实现动画效果 在上面我们实现了 Toast 的效果, 但是显示和隐藏都没有过度动画, 略显生硬那么我们加一些平移和透明度的动画, 然后对 componentDidMount 修改实现动画效果 在组件中增加两个变量
- moveAnim = new Animated.Value(height / 12);
- opacityAnim = new Animated.Value(0);
在之前内层 view 的样式中, 设置的 bottom 是 height/8 我们此处将 view 样式设置如下
style={[styles.textContainer, {bottom: this.moveAnim, opacity: this.opacityAnim}]}
然后修改 componentDidMount
- componentDidMount() {
- Animated.timing(
- this.moveAnim,
- {
- toValue: height / 8,
- duration: 80,
- easing: Easing.ease
- },
- ).start(this.timingDismiss);
- Animated.timing(
- this.opacityAnim,
- {
- toValue: 1,
- duration: 100,
- easing: Easing.linear
- },
- ).start();
- }
也就是 bottom 显示时从 height/12 到 height/8 移动, 时间是 80ms, 透明度从 0 到 1 转变执行时间 100ms 在上面我们看到有个 easing 属性, 该属性传的是动画执行的曲线速度, 可以自己实现, 在 Easing API 中已经有多种不同的效果大家可以自己去看看实现, 源码地址是 https://github.com/facebook/react-native/blob/master/Libraries/Animated/src/Easing.js, 自己实现的话直接给一个计算函数就可以, 可以自己去看模仿 #定义显示时间 在前面我们设置 Toast 显示 1000ms, 我们对显示时间进行自定义, 限定类型 number,
time: PropTypes.number
在构造方法中对时间的处理
time: props.time && props.time <1500 ? Toast.SHORT : Toast.LONG,
在此处我对时间显示处理为 SHORT 和 LONG 两种值了, 当然你可以自己处理为想要的效果 然后只需要修改 timingDismiss 中的时间 1000, 写为 this.state.time 就可以了 #组件更新 当组件已经存在时再次更新属性时, 我们需要对此进行处理, 更新 state 中的 message 和 time, 并清除定时器, 重新定时
- componentWillReceiveProps(nextProps) {
- this.setState({
- message: nextProps.message !== undefined ? nextProps.message : '',
- time: nextProps.time && nextProps.time < 1500 ? Toast.SHORT : Toast.LONG,
- })
- clearTimeout(this.dismissHandler)
- this.timingDismiss()
- }
组件注册
为了我们的定义的组件以 API 的形式调用, 而不是写在 render 方法中, 所以我们定义一个跟组件
- import React, {Component} from "react";
- import {StyleSheet, AppRegistry, View, Text} from 'react-native';
- viewRoot = null;
- class RootView extends Component {
- constructor(props) {
- super(props);
- console.log("constructor:setToast")
- viewRoot = this;
- this.state = {
- view: null,
- }
- }
- render() {
- console.log("RootView");
- return (<View style={styles.rootView} pointerEvents="box-none">
- {this.state.view}
- </View>)
- }
- static setView = (view) => {
- // 此处不能使用 this.setState
- viewRoot.setState({view: view})
- };
- }
- const originRegister = AppRegistry.registerComponent;
- AppRegistry.registerComponent = (appKey, component) => {
- return originRegister(appKey, function () {
- const OriginAppComponent = component();
- return class extends Component {
- render() {
- return (
- <View style={styles.container}>
- <OriginAppComponent/>
- <RootView/>
- </View>
- );
- };
- };
- });
- };
- const styles = StyleSheet.create({
- container: {
- flex: 1,
- position: 'relative',
- },
- rootView: {
- position: "absolute",
- left: 0,
- right: 0,
- top: 0,
- bottom: 0,
- flexDirection: "row",
- justifyContent: "center",
- }
- });
- export default RootView
RootView 就是我们定义的根组件, 实现如上, 通过 AppRegistry.registerComponent 注册 #包装供外部调用
- import React, {
- Component,
- } from 'react';
- import RootView from '../RootView'
- import ToastView from './ToastView'
- class Toast {
- static LONG = 2000;
- static SHORT = 1000;
- static show(msg) {
- RootView.setView(<ToastView
- message={msg}
- onDismiss={() => {
- RootView.setView()
- }}/>)
- }
- static show(msg, time) {
- RootView.setView(<ToastView
- message={msg}
- time={time}
- onDismiss={() => {
- RootView.setView()
- }}/>)
- }
- }
- export default Toast
Toast 中定义两个 static 变量, 表示显示的时间供外部使用然后提供两个 static 方法, 方法中调用 RootView 的 setView 方法将 ToastView 设置到根 view
- # 使用 首先导入上面的 Toast, 然后通过下面方法调用
- Toast.show("测试, 我是 Toast");
- // 能设置显示时间的 Toast
- Toast.show("测试",Toast.LONG);
好了文章介绍完毕如果想看完整代码, 可以进我 GitHub 查看文中若有不足或错误的地方欢迎指出最后新年快乐
来源: https://juejin.im/post/5aa66b66518825556f5512ed