如何用一个按钮关闭React Native模态
最近我一直在使用React Native,我遇到了一些未实现的功能,我需要将其添加到基本组件中。其中一个组件是React NativeModal 组件,用于在另一个视图之上显示一个视图。它没有承诺更多的功能,但它对实现你自己的弹出式窗口非常有用。
另一方面,由于它只是一个简单的模态,它不包括你对一个弹出式组件所期望的一切,比如一个X来关闭它,或者当你点击旁边的模态时关闭它。当你从一个模态 组件开始时,我将指导你如何添加这两个功能。
如何关闭一个模态
让我们从一个基本的App.js 开始,显示一个弹出式窗口。我还添加了一些样式,让它有一个阴影,这样弹出式窗口就可以看到了,还添加了大量的空白,这样我们就可以在以后通过点击旁边的弹出式窗口来测试关闭它。
import React, { useState } from 'react';
import { StyleSheet, Modal, Text, View } from 'react-native';
export default function App() {
const [modalVisible, setModalVisible] = useState(true);
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</text>
<Modal visible={modalVisible}>
<View style={styles.modal}>
<Text>
Popup content.
</Text>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
alignItems: "center",
justifyContent: "center",
},
modal: {
flex: 1,
margin: 50,
padding: 5,
backgroundColor: "white",
shadowColor: "black",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
});
Modal 是根据它的可见道具来显示的。现在,状态中的值始终为真,所以弹出窗口将始终是可见的。
即使没有按钮或其他改变状态的方法,在Android上也可以用后退按钮关闭Modal (根据文档,Apple TV上的菜单按钮应该有同样的效果,但我没有一个可以测试)。
按下按钮会调用modal的onRequestClose道具中指定的函数,所以让我们改变状态,在按下按钮时隐藏弹出窗口,这将显示应用程序的主屏幕。
<Modal
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}>
如何在React Native模态中添加关闭按钮
首先,我们必须添加按钮本身,这样我们就可以用它来关闭弹出窗口。在这种情况下,我想在弹出窗口的右上角添加一个小X,但它也可以在其他地方。
考虑到定位的工作方式,对此有两种选择:
- 定位绝对是把按钮放在右上角。然后你可以对其余的内容做任何你想做的事,但这样你就有可能让内容与X重叠,因为它超出了正常的布局流程。如果你想同时使用按钮旁边和下面的空间,这几乎是不可能的,这就导致我们有了第二个选择。
- 用flexbox定位按钮,在按钮的左边留出空间做标题。然后你可以分别填入标题和底部的内容。如果你要做的是一个弹出式的东西,有一个标题也是一个很标准的功能。
下面是添加了X的组件现在的样子:
import React, { useState } from 'react';
import { StyleSheet, Modal, Text, View, TouchableOpacity } from 'react-native';
export default function App() {
const [modalVisible, setModalVisible] = useState(true);
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Modal visible={modalVisible} onRequestClose={() => setModalVisible(false)}>
<View style={styles.modal}>
<View style={styles.modalHeader}>
<View style={styles.modalHeaderContent}><Text>Other header content</Text></View>
<TouchableOpacity><Text style={styles.modalHeaderCloseText}>X</Text></TouchableOpacity>
</View>
<View style={styles.modalContent}>
<Text>
Popup content.
</Text>
</View>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
alignItems: "center",
justifyContent: "center",
},
modal: {
flex: 1,
margin: 50,
padding: 5,
backgroundColor: "white",
shadowColor: "black",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
/* The content of the modal takes all the vertical space not used by the header. */
modalContent: {
flex: 1
},
modalHeader: {
flexDirection: "row",
},
/* The header takes up all the vertical space not used by the close button. */
modalHeaderContent: {
flexGrow: 1,
},
modalHeaderCloseText: {
textAlign: "center",
paddingLeft: 5,
paddingRight: 5
}
});
标题是一个灵活的容器,显示为一排,用flexGrow:1 ,表示它应该占据所有的剩余空间。
弹出窗口中的其他内容是一个flex:1 ,所以它占据了所有剩余的高度。
在这一点上,唯一剩下的就是把按钮连接起来,让它关闭弹出窗口,就像我们之前在onRequestClose 事件中设置的那样。
<TouchableOpacity onPress={() => setModalVisible(false)}>
<Text style={styles.modalHeaderCloseText}>X</Text>
</TouchableOpacity>
如何通过点击外部来关闭模态
为了通过点击外部来关闭模态,我们需要一个额外的组件来捕捉这些点击。
另一方面,我们不希望这个组件捕捉为子组件准备的点击:点击弹出窗口本身不应该关闭它。通过检查event.target == event.currentTarget ,我们验证了被选中的项目是组件本身,而不是它的一个子组件。
我使用了一个Pressable组件,因为我不希望在触碰弹出式窗口之外时,出现TouchableOpacity所带来的 "调光 "效果。这个新的Pressable 组件包裹了我们之前在modal中定义的所有组件。
这是完成的弹出窗口,有一些额外的边框来显示标题和弹出窗口的内容。
import React, { useState } from 'react';
import { StyleSheet, Modal, Text, View, Pressable, TouchableOpacity } from 'react-native';
export default function App() {
const [modalVisible, setModalVisible] = useState(true);
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Modal
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}>
<Pressable style={styles.outsideModal}
onPress={(event) => { if (event.target == event.currentTarget) {
setModalVisible(false); } }} >
<View style={styles.modal}>
<View style={styles.modalHeader}>
<View style={styles.modalHeaderContent}><Text>Other header content</Text></View>
<TouchableOpacity onPress={() => setModalVisible(false)}>
<Text style={styles.modalHeaderCloseText}>X</Text>
</TouchableOpacity>
</View>
<View style={styles.modalContent}>
<Text>
Popup content.
</Text>
</View>
</View>
</Pressable>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
alignItems: "center",
justifyContent: "center",
},
modal: {
flex: 1,
margin: 50,
padding: 5,
backgroundColor: "white",
shadowColor: "black",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
/* The content of the modal takes all the vertical space not used by the header. */
modalContent: {
flex: 1,
borderWidth: 1,
borderColor: "black"
},
modalHeader: {
flexDirection: "row",
borderWidth: 1,
borderColor: "black"
},
/* The header takes up all the vertical space not used by the close button. */
modalHeaderContent: {
flexGrow: 1,
},
modalHeaderCloseText: {
textAlign: "center",
paddingLeft: 5,
paddingRight: 5
},
outsideModal: {
backgroundColor: "rgba(1, 1, 1, 0.2)",
flex: 1,
}
});
请注意,这有一个小小的限制:Pressable 或TouchableOpacity 的背景颜色不能被设置为透明或半透明的值,所以弹出窗口下的内容将不再可见。
下一步要让这个问题变得更好,就是把Modal 组件和它的所有内容打包成一个新的组件,可以在你的应用程序中重复使用,这样你就可以在弹出窗口中插入任何标题或内容,但这不在本文的范围之内。