react-native-root-toast自定義彈窗
基於版本 “react-native-root-toast”: “^3.2.1”
Toast的調用方法:
官方api提供兩種調用方式
1、Calling api
import Toast from 'react-native-root-toast';
// Add a Toast on screen.
let toast = Toast.show('This is a message', {
duration: Toast.durations.LONG,
position: Toast.positions.BOTTOM,
shadow: true,
animation: true,
hideOnPress: true,
delay: 0,
onShow: () => {
// calls on toast\`s appear animation start
},
onShown: () => {
// calls on toast\`s appear animation end.
},
onHide: () => {
// calls on toast\`s hide animation start.
},
onHidden: () => {
// calls on toast\`s hide animation end.
}
});
// You can manually hide the Toast, or it will automatically disappear after a `duration` ms timeout.
setTimeout(function () {
Toast.hide(toast);
}, 500);
2、Using a Component
import React, {Component} from 'react-native';
import Toast from 'react-native-root-toast';
class Example extends Component{
constructor() {
super(...arguments);
this.state = {
visible: false
};
}
componentDidMount() {
setTimeout(() => this.setState({
visible: true
}), 2000); // show toast after 2s
setTimeout(() => this.setState({
visible: false
}), 5000); // hide toast after 5s
};
render() {
return <Toast
visible={this.state.visible}
position={50}
shadow={false}
animation={false}
hideOnPress={true}
>This is a message</Toast>;
}
}
Demo中使用方法
1、簡單提示文字
import Toast from 'react-native-root-toast';
...
Toast.show('正在加載...', {
position: Toast.positions.CENTER, // toast位置
});
效果
2、自定義彈窗
renderToast() {
return (
<View style={{ borderRadius: 3, backgroundColor: 'rgba(0, 0, 0, 0.8)' }}>
<View style={styles.toastWrap}>
<Image style={styles.toastIcon} source={require('../../assets/image/toast/check-circle.png')} resizeMode="cover" />
</View>
<View style={styles.textWrap}>
<Text style={styles.text}>正在加載...</Text>
</View>
</View>
);
}
showToast() {
Toast.show(this.renderToast(), {
position: Toast.positions.CENTER, // toast位置
});
}
但是發現自定義彈窗是按照設計的窗口來展現的,但是文字不顯示。
效果
查了下包內源碼。
在react-native-root-toast/lib/Toast.js中;
class Toast extends Component {
static displayName = 'Toast';
static propTypes = ToastContainer.propTypes;
static positions = positions;
static durations = durations;
static show = (message, options = {position: positions.BOTTOM, duration: durations.SHORT}) => {
return new RootSiblings(<ToastContainer
{...options}
visible={true}
>
{message}
</ToastContainer>);
};
static hide = toast => {
if (toast instanceof RootSiblings) {
toast.destroy();
} else {
console.warn(`Toast.hide expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof toast}\` instead.`);
}
};
_toast = null;
componentWillMount = () => {
this._toast = new RootSiblings(<ToastContainer
{...this.props}
duration={0}
/>);
};
componentWillReceiveProps = nextProps => {
this._toast.update(<ToastContainer
{...nextProps}
duration={0}
/>);
};
componentWillUnmount = () => {
this._toast.destroy();
};
render() {
return null;
}
}
如果show傳入message的是自定義組件,那message會被包裹在在ToastContainer中,作爲ToastContainer的props.chilren
在react-native-root-toast/lib/ToastContainer.js中;
render() {
let { props } = this;
const { windowWidth } = this.state;
let offset = props.position;
const { windowHeight, keyboardScreenY } = this.state;
const keyboardHeight = Math.max(windowHeight - keyboardScreenY, 0);
let position = offset ? {
[offset < 0 ? 'bottom' : 'top']: offset < 0 ? (keyboardHeight - offset) : offset
} : {
top: 0,
bottom: keyboardHeight
};
return (this.state.visible || this._animating) ? <View
style={[
styles.defaultStyle,
position
]}
pointerEvents="box-none"
>
<TouchableWithoutFeedback
onPress={() => {
typeof this.props.onPress === 'function' ? this.props.onPress() : null
this.props.hideOnPress ? this._hide() : null
}}
>
<Animated.View
style={[
styles.containerStyle,
{ marginHorizontal: windowWidth * ((1 - TOAST_MAX_WIDTH) / 2) },
props.containerStyle,
props.backgroundColor && { backgroundColor: props.backgroundColor },
{
opacity: this.state.opacity
},
props.shadow && styles.shadowStyle,
props.shadowColor && { shadowColor: props.shadowColor }
]}
pointerEvents="none"
ref={ele => this._root = ele}
>
<Text style={[
styles.textStyle,
props.textStyle,
props.textColor && { color: props.textColor }
]}>
{this.props.children}
</Text>
</Animated.View>
</TouchableWithoutFeedback>
</View> : null;
}
可以看到外部傳入的message作爲this.props.children包裹在Text組件中的。
懷疑是不是下面這種嵌套結構不行,在界面中測試了一下確實不可以
<Text >
<View>
<Text>Hello</Text>
</View>
</Text>
所以這裏對ToastContainer做了下修改
<Animated.View
style={[
styles.containerStyle,
{ marginHorizontal: windowWidth * ((1 - TOAST_MAX_WIDTH) / 2) },
props.containerStyle,
props.backgroundColor && { backgroundColor: props.backgroundColor },
{
opacity: this.state.opacity
},
props.shadow && styles.shadowStyle,
props.shadowColor && { shadowColor: props.shadowColor }
]}
pointerEvents="none"
ref={ele => this._root = ele}
>
{/* <Text style={[
styles.textStyle,
props.textStyle,
props.textColor && { color: props.textColor }
]}>
{this.props.children}
</Text> */}
{React.isValidElement(this.props.children) ? this.props.children :
<Text style={[
styles.textStyle,
props.textStyle,
props.textColor && { color: props.textColor }
]}>
{this.props.children}
</Text>}
</Animated.View>
測試下效果
OK 可以顯示完整的自定義彈窗了!