65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在React Native中通过按钮关闭模态框

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2021年9月11日

CPOL

4分钟阅读

viewsIcon

48249

在 React Native 中添加一个关闭按钮,并通过点击它旁边的区域来关闭模态框。

引言

最近我一直在使用 React Native,并且遇到了一些需要添加到基本组件中的未实现的功能。 其中一个组件是 React Native Modal 组件,用于在一个视图之上显示另一个视图。 它没有承诺比这更多的功能,但它对于实现您自己的弹出窗口非常有用。

另一方面,由于它只是一个简单的模态框,它不包含您对弹出组件的期望,例如关闭它的 X,或者在您点击它旁边时关闭模态框。 我将向您介绍如何在使用 Modal 组件开始时添加这两个功能。

如果您赶时间,也可以在我的 GitHub 上找到最终结果,网址为 https://github.com/CindyPotvin/react-native-popup

如何关闭模态框

让我们从一个显示弹出窗口的基本 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 是根据其可见属性显示的。 现在,状态中的值始终为 true,因此弹出窗口将始终可见。

即使没有按钮或更改状态的另一种方法,也可以使用后退按钮在 Android 上关闭 Modal(Apple TV 上的菜单按钮应该具有相同的效果,根据文档,但我没有一个可以测试)。

按下按钮会调用在 modal 的 onRequestClose 属性中指定的函数,因此让我们更改状态以隐藏弹出窗口,当按下时,这将显示应用程序的主屏幕

<Modal
  visible={modalVisible}
  onRequestClose={() => setModalVisible(false)}>

如何向 React Native Modal 添加关闭按钮

首先,我们必须添加按钮本身,然后才能使用它来关闭弹出窗口。 在这种情况下,我想在弹出窗口的右上角添加一个小 X,但它可以在其他任何地方。

考虑到定位方式,有两种选择

  • 将按钮绝对定位在右上角。 然后,您可以对内容的其余部分做任何您想做的事情,但是您会冒着内容与 X overlapping 的风险,因为它超出了正常的布局流程。 如果您想同时使用按钮旁边和下方的空间,那几乎是不可能的,这导致了第二种选择。
  • 使用 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
  }
});

标题是一个 flex 容器,显示为一行,其中 flexGrow:1 表示它应该占用所有剩余空间。

弹出窗口中其余的内容是一个 flex:1 项目,因此它占用了所有剩余的高度。

此时剩下的唯一一件事是将按钮连接起来,以便它像我们在之前的 onRequestClose 事件中设置的那样关闭弹出窗口

<TouchableOpacity onPress={() => setModalVisible(false)}>
  <Text style={styles.modalHeaderCloseText}>X</Text>
</TouchableOpacity>  

如何通过点击外部区域关闭 Modal

要通过点击外部区域也关闭模态框,我们需要一个额外的组件来捕捉这些点击。

另一方面,我们不希望此组件捕捉旨在用于子组件的点击:点击弹出窗口本身不应该关闭它。 通过检查 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,
  }
});

请注意,这有一个小小的限制:PressableTouchableOpacity 的背景颜色不能设置为透明或半透明值,因此弹出窗口下的内容将不再可见。

要使其更好的下一步是,将 Modal 组件及其所有内容打包到一个新组件中,该组件可以在您的应用程序中重复使用,以便您可以在弹出窗口中插入任何标题或内容,但这超出了当前文章的范围。

如果您想自己执行最终版本并进行尝试,您可以在 GitHub 上查看它,网址为 https://github.com/CindyPotvin/react-native-popup,也可以在 Expo Snack 上查看,网址为 https://snack.expo.dev/@cindyptn/react-native-popup-with-x-button

历史

  • 2021年9月11日:初始版本
© . All rights reserved.