通过添加根节点的形式实现的toast,几乎满足整个项目的需求场景。
需要用到插件
import RootSiblings from 'react-native-root-siblings';
在控制台下载
npm install react-native-root-siblings --save
下载完成之后就可以使用了。
接下来添加copy代码作为一个组件放在项目的工具文件夹里面
import React from 'react';
import {
Text,
View,
StyleSheet,
Dimensions
} from 'react-native';
const { width } = Dimensions.get('window')
import RootSiblings from 'react-native-root-siblings';
const siblings_ = new RootSiblings()
let setTime = null;
// Toast公用版本 (添加根节点)
class Toast {
static show(massege, duration = 3000, callback) {
siblings_.update(
<View style={styles.container}>
<Text style={styles.text}>
{massege}
</Text>
</View>
);
let tempDuration = 3000 // 执行时间
if (typeof duration === 'number') {
tempDuration = duration
}
setTime !== null ? clearTimeout(setTime) : null
setTime = setTimeout(() => {
callback && callback() // 回调
if (duration && typeof duration === 'function') {
duration && duration()
}
siblings_.destroy();
clearTimeout(setTime)
}, tempDuration);
}
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
width: width / 2,
left: (width / 2) / 2,
bottom: 80,
padding: 15,
backgroundColor: '#666',
borderRadius: 5,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
},
text: {
color: '#fff'
}
})
export default Toast
在APP.js里面注册成为全局使用
APP.js
import Toast from '@/utils/Toast.js';
global.Toast =Toast // 全局Toast弹框
以下的是modal版本
业务需要专门看了react native的Animate动画做出一个Toast提示框
当前的toast是使用modal弹出框,作为最高层级。在日常业务中不能完整全部场景。
// Toast组件
import React from 'react';
import { Animated, Text, View,Easing,StyleSheet,Dimensions } from 'react-native';
const {width} = Dimensions.get('window')
export default class ToastView extends React.Component {
state = {
fadeAnim: new Animated.Value(0), // 透明度初始值设为0
}
// 执行动画
shwoToast = (duration = 3000) => {
Animated.sequence([
Animated.timing( // 随时间变化而执行动画
this.state.fadeAnim, // 动画中的变量值
{
easing:Easing.linear,
toValue: 1, // 透明度最终变为1,即完全不透明
duration: duration, // 让动画持续一段时间
}
),
]).start(); // 开始执行动画
}
render() {
let { fadeAnim } = this.state;
let {showToast,text,duration} = this.props
if (showToast) { // 布尔值判断是否显示Toast
this.state.fadeAnim.setValue(0) // 必须要每次执行都变更一次为0,才能使得动画从0隐藏到1显示
this.shwoToast(duration) // 执行动画
}
const opacity = fadeAnim.interpolate({
inputRange: [0, 0.5,1], // 显示
outputRange: [0, 5,0] // 隐藏
});
return (
<Animated.View // 使用专门的可动画化的View组件
style={{
...styles.container,
opacity: opacity, // 将透明度指定为动画变量值
}}
>
<Text style={styles.text}>{text}</Text>
</Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
// flex:1,
position:'absolute',
bottom:80,
backgroundColor:'#ddd',
borderRadius:10,
height:'auto',
padding:15,
width:140,
left:(width/2)-70
},
text: {
textAlign: 'center'
}
})
使用方法
import React, { Component } from 'react';
import { StyleSheet, Text, View, ToastAndroid, Animated, FlatList, Dimensions } from 'react-native';
import styles from './Style'
import ToastView from '../../../components/toast/Index' // 引用
// 首页
export default class Index extends Component {
constructor(props) {
super(props);
this.state = {
showToast: false
}
}
showtoast = () => {
this.setState({
showToast:true
})
}
// ToastView 组件可以传三个值 showToast:是否显示(布尔值) text:提示的内容(字符串) duration:执行时间(毫秒)
render() {
let { list, showToast } = this.state
return (
<View style={styles.container}>
<ToastView
showToast={showToast}
text="显示内容"
duration={2000} />
<Text onPress={() => this.showtoast()}>点击显示Toast框</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
flatList: {
flexDirection:'row'
}
});
效果
可以全局使用也可以单独引入
新建一个Toast文件
// Toast
import React from 'react';
import { Animated, Text, View, Easing, Dimensions, Modal,StyleSheet } from 'react-native';
const widthScreen = Dimensions.get('window').width
export default class Toast extends React.Component {
state = {
fadeAnim: new Animated.Value(0), // 透明度初始值设为0
text: '', // 显示文本
refToast: false, // 是否通过ref绑定
timer: '', // 定时器
callback:null, // 回调
}
// 执行动画
shwoToast = (duration = 2000) => {
Animated.sequence([
Animated.timing( // 随时间变化而执行动画
this.state.fadeAnim, // 动画中的变量值
{
easing: Easing.linear,
toValue: 1, // 透明度最终变为1,即完全不透明
duration: duration, // 让动画持续一段时间
}
),
]).start(); // 开始执行动画
}
show = (text, duration,callback) => {
this.setTiner(duration)
this.setState({ text, refToast: true })
this.state.fadeAnim.setValue(0) // 必须要每次执行都变更一次为0,才能使得动画从0隐藏到1显示
this.shwoToast() // 执行动画
callback&&callback() // 回调
}
setTiner = () => {
this.state.timer = setTimeout(() => {
this.setState({ refToast: false })
clearTimeout(this.state.timer)
}, 2000);
}
render() {
let { fadeAnim, refToast } = this.state
let { showToast, duration } = this.props
let { width, left } = styles.container
let text = refToast ? this.state.text : this.props.text
if (!refToast && showToast) { // 布尔值判断是否显示Toast
this.state.fadeAnim.setValue(0) // 必须要每次执行都变更一次为0,才能使得动画从0隐藏到1显示
this.show(text, duration) // 执行动画
showToast = false // 执行之后要变为false,不在执行
}
// 检查显示文字内容过多宽度变大
if (text && text.length > 14) {
width = 200
left = (widthScreen / 2) - 100
} else {
width = 140
left = (widthScreen / 2) - 70
}
const opacity = fadeAnim.interpolate({
inputRange: [0, 0.5, 1], // 显示
outputRange: [0, 5, 0] // 隐藏
});
return (
<View>
<Modal
animationType="none"
transparent={refToast}
visible={refToast}
>
<Animated.View // 使用专门的可动画化的View组件
style={{
...styles.container,
opacity: opacity, // 将透明度指定为动画变量值
width,
left
}}
>
<View style={styles.bodyView}>
<Text style={styles.bodyText}>{text}</Text>
</View>
</Animated.View>
</Modal>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
bottom: 80,
backgroundColor: '#ddd',
borderRadius: 10,
height: 'auto',
padding: 15,
width: 140,
left: (widthScreen / 2) - 70,
zIndex: 9991,
},
bodyView: {},
bodyText: {
textAlign: 'center'
}
})
接下来我们需要在app.js中引用并且注册组件
1、我们需要引入toast文件
// app.js
import ToastView from './src/components/toast/Index';
2、在app.js组件中注册
// app.js
render() {
return (
<Provider store={store}>
<View style={styles.app}>
<ToastView ref="toast" />
<StackNavigator />
</View>
</Provider>
);
}
3、需要定义一个全局变量,用来全局使用
// app.js
global.Toast = '' // 全局Toast弹框
4、在节点渲染完成之后需要获取refs的绑定
app.js
componentDidMount() {
Toast = this.refs.toast // 绑定Toast节点
}
5、完整代码,我这里包含了redux和路由
// app.js
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View, BackHandler, ToastAndroid, Dimensions } from 'react-native';
import StackNavigator from './src/router/StackNavigator';
import { Provider } from 'react-redux';
import store from './src/redux/store';
import ToastView from './src/components/toast/Index'; // 引入Toast文件
const screenWidth = Dimensions.get('window').width;
import PreventDoublePress from './src/utils/PreventDoublePress'
// 禁止警告
console.disableYellowBox = true;
console.warn('YellowBox is disabled.');
// 打包之后清除所有console警告
if (!__DEV__) {
global.console = {
info: () => { },
log: () => { },
warn: () => { },
debug: () => { },
error: () => { }
};
}
// 全局
global.loginToken = '' // 登录token
global.Toast = '' // 全局Toast弹框
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
lastBackPressed: 0
}
}
// 页面创建的时候对物理按钮进行监听
componentWillMount() {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', this.BackHandler);
}
}
// 页面摧毁的时候销毁监听
componentWillUnmount() {
if (Platform.OS === 'android') {
BackHandler.removeEventListener('hardwareBackPress', this.BackHandler);
}
}
componentDidMount() {
Toast = this.refs.toast // 绑定Toast节点
}
// 物理返回键 事件触发
BackHandler = () => {
if (this.state.lastBackPressed && this.state.lastBackPressed + 2000 >= Date.now()) {
BackHandler.exitApp()
return false
}
this.state.lastBackPressed = Date.now()
ToastAndroid.show('再按一次退出应用', ToastAndroid.SHORT)
return true
}
render() {
return (
<Provider store={store}>
<View style={styles.app}>
<ToastView ref="toast" />
<StackNavigator />
</View>
</Provider>
);
}
}
const styles = StyleSheet.create({
app: {
flex: 1,
flexDirection: 'row'
},
toast: {
position: 'absolute',
bottom: 50,
left: screenWidth / 2,
backgroundColor: '#aaa',
width: 100,
height: 'auto',
zIndex: 999
},
toastText: {
// color:'#000'
}
});
使用方法:在任意一个业务页面中 直接 Toast.show(显示内容
)
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { getDate } from '../../../redux/actions'
import { connect } from 'react-redux';
import styles from './Style'
// 首页
class Index extends Component {
showtoast = () => {
Toast.show(`Toast显示内容`)
}
render() {
return (
<View style={styles.container}>
<Text onPress={() => this.showtoast()}>点击显示Toast框</Text>
</View>
);
}
}
const mapStateToProps = state => ({
store: state.store
})
export default connect(mapStateToProps)(Index);
Toast.show() 里面可以提交有三个参数
参数1:显示的文本
参数2: 显示时长 默认2000毫秒
参数3:回调函数
使用方法:
Toast.show('Toast内容',2000,() => {
// 做些什么
})
最终效果
也可以在当前页面单独引用
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { getDate } from '../../../redux/actions'
import { connect } from 'react-redux';
import styles from './Style'
import Toast from '../../../components/toast/Index'
// 首页
class Index extends Component {
constructor(props) {
super(props);
this.state = {
text:'',
show:false
}
}
showtoast = () => {
this.setState({
text:'Toast显示内容',
show:true
})
}
render() {
return (
<View style={styles.container}>
<Toast
text={this.state.text}
show={this.state.show}
/>
<Text onPress={() => this.showtoast()}>点击显示Toast框</Text>
</View>
);
}
}
const mapStateToProps = state => ({
store: state.store
})
export default connect(mapStateToProps)(Index);
Toast弹框需要显示图标、图标、背景颜色等等,只需要在Toast文件中自定义该样式就可以了。
<View style={styles.bodyView}>
<Text style={styles.bodyText}>{text}</Text>
</View>