通过单击叠加关闭反应本机模式?

时间:2016-11-08 09:17:00

标签: javascript react-native

transparent选项为true时点击叠加层是否可以关闭react native modal?文档没有提供任何相关信息。有可能吗?

16 个答案:

答案 0 :(得分:43)

如果我理解正确,你想在用户点击它时关闭模态,对吧?

如果是的话,我前段时间搜索过这个问题,我记得的唯一解决方案就是这个(就是那个 我到目前为止一直在使用):

render() { 
  if (!this.state.modalVisible)
    return null
  return (
     <View>
        <Modal 
          animationType="fade"
          transparent={true}
          visible={this.state.modalVisible}
          onRequestClose={() => {this.setModalVisible(false)}}
        >
          <TouchableOpacity 
            style={styles.container} 
            activeOpacity={1} 
            onPressOut={() => {this.setModalVisible(false)}}
          >
            <ScrollView 
              directionalLockEnabled={true} 
              contentContainerStyle={styles.scrollModal}
            >
              <TouchableWithoutFeedback>
                <View style={styles.modalContainer}>
                  // Here you put the content of your modal.
                </View>
              </TouchableWithoutFeedback>
            </ScrollView>
          </TouchableOpacity>   
        </Modal> 
     </View>
  )
} 

// Then on setModalVisible(), you do everything that you need to do when closing or opening the modal.
setModalVisible(visible) {
    this.setState({
        modalVisible: visible,
    })
}

解释

这基本上是在整个屏幕中使用TouchableOpacity来获取用户单击以关闭模式。 TouchableWithoutFeedback是为了避免TouchableOpacity在Modal内部工作。

如果您有更好的解决方案,请在此处分享。

答案 1 :(得分:4)

另一种解决方案:

// Modal.js
import React from 'react';
import {
  TouchableWithoutFeedback,
  StyleSheet,
  Modal,
  View,
} from 'react-native';
import t from 'prop-types';


class MyModal extends React.Component {
  static propTypes = {
    children: t.node.isRequired,
    visible: t.bool.isRequired,
    dismiss: t.func.isRequired,
    transparent: t.bool,
    animationType: t.string,
  };

  static defaultProps = {
    animationType: 'none',
    transparent: true,
  };

  render() {
    const { props } = this;
    return (
      <View>
        <Modal
          visible={props.visible}
          transparent={props.transparent}
          onRequestClose={props.dismiss}
          animationType={props.animationType}
        >
        <TouchableWithoutFeedback onPress={props.dismiss}>
          <View style={styles.modalOverlay} />
        </TouchableWithoutFeedback>

        <View style={styles.modalContent}>
          {props.children}
        </View>
        </Modal>
      </View>
    );
  }
}


const styles = StyleSheet.create({
  modalContent: {
    flex: 1,
    justifyContent: 'center',
    margin: '5%',
  },
  modalOverlay: {
    position: 'absolute',
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
    backgroundColor: 'rgba(0,0,0,0.5)'
  },
});


export default MyModal;

用法示例:

// SomeScreen.js
import React from 'react';
import { View, Text, Button } from 'react-native';

import Modal from './Modal';


class SomeScreen extends React.Component {
  state = {
    isModalVisible: false,
  };

  showModal = () => this.setState({ isModalVisible: true });
  hideModal = () => this.setState({ isModalVisible: false });

  render() {
    return (
      <View>
        <Button onPress={this.showModal} />
        <Modal
          visible={this.state.isModalVisible}
          dismiss={this.hideModal}
        >
          <Text>Hello, I am modal</Text>
        </Modal>
      </View>
    );
  }
}

答案 2 :(得分:3)

我们可以通过添加:

来解决这个问题
import { TouchableOpacity } from 'react-native';
    <TouchableOpacity onPress={()=>this.setState({modalVisibilty:false})}>
    <View style={{opacity:0, flex:1 }}/>
    </TouchableOpacity>

在窗口下方,另一个在上面,并更改布局样式以适合您的屏幕。

说明:

您将制作2个大隐藏按钮以捕捉用户触摸并将模态可见性状态更改为false。

答案 3 :(得分:1)

这是我的解决办法,

import React from "react";
import {Modal, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, View} from "react-native";

// make sure the styles props is passed to the model and contains modalContainer, as well as the childrenContainer style objects.

export default class DismissibleModal extends React.Component {
    render() {
        return (
            <Modal
              animationType="slide"
              transparent={true}
              visible={this.props.visible}
              onRequestClose={() => {this.props.dismiss()}}>
              <TouchableOpacity
                style={Styles.modal}
                activeOpacity={1}
                onPressOut={() => {this.props.dismiss()}}>
                    <View style={[this.props.styles.modalContainer]}>
                        <TouchableWithoutFeedback>
                            <View style={[this.props.styles.childrenContainer]}>
                                {this.props.children}
                            </View>
                        </TouchableWithoutFeedback>
                    </View>
              </TouchableOpacity>
        </Modal>
        )
    }

}



const Styles = StyleSheet.create({
    modal: {
        flex: 1,
        backgroundColor: 'transparent',
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center'
    },
    modalView: {
        backgroundColor: "white",
        borderRadius: 10,
        padding: 20,
        paddingTop: 20,
        alignItems: "center",
        shadowColor: "#000",
        shadowOffset: {
            width: 0,
            height: 9,
        },
        shadowOpacity: 0.50,
        shadowRadius: 12.35,

        elevation: 19,
  },
});

答案 4 :(得分:1)

简单的解决方案。您需要一个touchableOpacity来单击外部,而另一个touchableOpacity来进行实际的模态操作,而在onPress上什么都不做。

import React, { Component } from 'react'
import { View, StyleSheet, TouchableOpacity, Modal} from 'react-native';

export class Modal extends Component {
    constructor(props){
        this.state = { modalVisible : true}
    }
    render() {
        return (
            <View>
                <Modal
                    animationType="slide"
                    transparent={true}
                    visible={this.state.modalVisible}
                    onRequestClose={() => { this.setState({modalVisible: false})
                    }}
                  >
                    <TouchableOpacity style={styles.modalContainer} onPress={() => { this.setState({ modalVisible : false})}}>
                        <TouchableOpacity style={styles.modal} onPress={() => console.log('do nothing')} activeOpacity={1} >
                            Modal Content...
                        </TouchableOpacity>
                    </TouchableOpacity>
                </Modal>
            </View>
        )
    }
}


const styles = StyleSheet.create({
    modalContainer: {
      flex: 1,
      justifyContent: 'center',
      alignItems: 'center',
    },
    modal: {
      width: 155,
      height: 300
    },
});

export default Modal;

activeOpacity = {1}只是消除了touchableOpacity淡入淡出效果

答案 5 :(得分:1)

@Gui Herzog 答案相当不错,但是将TouchableOpacity 设为父组件时,它会在涉及到子组件时处理冲突。

TouchableOpacity 中包含多个组件不是一个好习惯,否则 TouchableOpacity 父组件中的所有组件都是可点击的,解决此问题的最佳方法是将 TouchableOpacity 组件放入绝对位置,100% 宽度和高度在屏幕上。

这里有一些例子: Modal.js

export default function(props){
    const { width, height } = Dimensions.get('window');
    const hp = hp => (hp / 100) * height;
    const wp = wp => (wp / 100) * width;
    const size = size => Math.round((size / 100) * width);
    return (
        <KeyboardAvoidingView>
            <TouchableOpacity
                style={{ position: 'absolute', width: wp(100), height: hp(100) }}
                onPress={props.onTouchOutSide}
            />
            <ScrollView>
                {/*...child*/}
            </ScrollView>
        </KeyboardAvoidingView>
    )
}

欢迎你。

答案 6 :(得分:0)

        <Modal
            animationType="slide"
            closeOnClick={true}
            transparent={true}
            visible={this.state.modalVisible}
            >
            <TouchableOpacity onPress={() => { this.setModalVisible(!this.state.modalVisible)}} style={{flex:1, justifyContent:'center', alignItems:'center',}}>
                <View style={{flex:0.2,backgroundColor:'white', margin:20, borderRadius:20, borderWidth:2, borderColor:'gray'}}>
                    <Text style={{margin:20}}>모달 테스트</Text>
                </View>
            </TouchableOpacity>
        </Modal>

此代码是我的解决方案。

答案 7 :(得分:0)

<Modal isVisible={this.state.isVisible}
onBackdropPress={() => this.setState({ isVisible: false })}>
<View style={{ flex: 1 }}>
<Text>I am the modal content!</Text>
</View>
</Modal>

答案 8 :(得分:0)

这是我的简单实现:

<TouchableWithoutFeedback onPress={}> // Code to close your modal goes here
    <View style={styles.background}> // The view to drawn the background
            <View
                onStartShouldSetResponder={() => true}
                style={styles.container}
            > // The view to drawn your modal
            // Your content
            </View>
        </Androw>
    </View>
</TouchableWithoutFeedback>

我使用TouchableWithoutFeedback,因为我不想在单击时更改背景颜色。我还在模态视图上添加了onStartShouldSetResponder,以防止在视图内部单击时关闭模态。

我也不使用Modal组件,因为我是使用react-navigation完成的。

答案 9 :(得分:0)

使用简单代码的简单解决方案(如果您使用的是expo
这是一个完整的组件,您只需复制粘贴即可使其正常工作。

//MyModal.js

import React from 'react';
import { BlurView } from 'expo-blur';
import { View, StyleSheet, Modal, TouchableWithoutFeedback } from 'react-native';

export const MyModal = ({ children, visible, onRequestClose, onPressOverlay, }) => {
  return (
     <Modal
      visible={visible}
      transparent
      animationType='none'
      onRequestClose={onRequestClose}
     >
       <TouchableWithoutFeedback onPress={onPressOverlay}>
         <BlurView
           style={{ ...StyleSheet.absoluteFill }}
           tint='dark'
           intensity={100}
         />
       </TouchableWithoutFeedback>
       <View style={styles.container}>
         {children}
       </View>
    </Modal>
  );
};

const styles = StyleSheet.create({
  container: {
    height: '100%',
    width: '100%',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

现在,您可以将其导入到您的工作空间并像这样使用它。
我正在使用功能组件useState钩子来显示或隐藏模式

//yourScreen.js

import React, { useState } from 'react';
import { View, Button } from 'react-native';
import { MyModal } form './path/to/MyModal.js';

const YourScreen = () => {
  const [modalVisible, setModalVisible] = useState(false);
  return (
    <View style={{ flex:1 }}>
      <MyModal
        visible={modalVisible}
        onRequestClose={()=>{
          setModalVisible(false);
        }}
        onPressOverlay={()=>{
          setModalVisible(!modalVisible);
        }}
      >
        // your modal content goes here
      </MyModal>
      <Button
        title='Show Modal' 
        onPress={()=>{
          setModalVisible(!modalVisible);
        }}
      />
    </View>
  );
}

export default YourScreen;

答案 10 :(得分:0)

如果您使用诸如mobxredux之类的商店解决方案, 您只需解决商店中的标志即可。

下面是父母的代码,

      <Modal
        animationType="fade"
        transparent={true}
        visible={uiControlStore.dialogVisible}
        onRequestClose={() => {
          uiControlStore.dialogVisible = false;
        }}
      >
        <View
          style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0,0,0,0.6)' }}
          onTouchStart={() => {
            if (!uiControlStore.childClicked) uiControlStore.dialogVisible = false;
            uiControlStore.childClicked= false;
          }}
        >
          <YourCustomDialog />
        </View>
      </Modal>

和下面是孩子的代码。 (使用mobx)

const YourCustomDialog: React.FC = observer(() => {
  const { uiControlStore } = useStores();

  return (
    <View
      style={[styles.container, styles.border]}
      onTouchStart={() => {
        uiControlStore.childClicked= true;
      }}
    >
    ...
  )
}

答案 11 :(得分:0)

我知道我参加这个聚会很晚。但是我偶然发现了这个话题,Gui Herzog的答案正是我想要的。如果您不想添加任何外部依赖关系,那就应该这样做。谢谢!

但是,我想在组件中包括一些可选的负/正操作按钮,然后从react-native-paper中获取它们作为材质样式。 那是我意识到react-native-paper可能也有模式的时候。

这是他们的文档: https://callstack.github.io/react-native-paper/modal.html
他们也有对话框的组件 https://callstack.github.io/react-native-paper/dialog.html

因此,我最终使用了纸制Dialog,如果您打算在整个应用程序中使用该库,那是非常值得的。默认情况下,Dialog和Modal都可以处理外部点击。


这是在意识到Dialog组件已经存在之前创建的Dialog组件。

我仍然会保留我在这里实现的内容,因为我认为这是一个很好的模板。

该组件在打字稿中。确保更新@types/react-native,否则您可能会看到一些“没有超载匹配此调用”错误。

import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {Button, Modal, Portal} from 'react-native-paper';

interface Action {
  action: () => void;
  text: string;
}

interface Props {
  closePressed: () => void;
  negativeAction?: Action;
  positiveAction?: Action;
  title?: string;
  text: string;
  visible: boolean;
}

export const Dialog: React.FC<Props> = ({
  closePressed,
  negativeAction,
  positiveAction,
  title,
  text,
  visible,
}) => {
  return (
    <Portal>
      <Modal
        visible={visible}
        onDismiss={closePressed}
        contentContainerStyle={styles.modalContainer}>
        <View style={styles.header}>
          {title && (
            <Text style={{fontWeight: 'bold', fontSize: 18, marginBottom: 10}}>
              {title}
            </Text>
          )}
          <Text style={styles.contentText}>{text}</Text>
        </View>
        <View style={styles.buttonContainer}>
          {negativeAction && (
            <Button mode="text" onPress={negativeAction.action}>
              {negativeAction.text}
            </Button>
          )}
          {positiveAction && (
            <Button mode="text" onPress={positiveAction.action}>
              {positiveAction.text}
            </Button>
          )}
        </View>
      </Modal>
    </Portal>
  );
};

const styles = StyleSheet.create({
  modalContainer: {
    borderRadius: 5,
    backgroundColor: 'white',
    padding: 10,
    margin: 20,
  },
  header: {padding: 20},
  contentText: {color: 'grey'},
  buttonContainer: {
    flexDirection: 'row',
    justifyContent: 'flex-end',
    paddingTop: 20,
  },
});

答案 12 :(得分:0)

这是我完美的解决方案

模式代码:

const ListInModal = ({ showListModal, onClose, data, onSelectItem }) => {
  return (
    <Modal animationType="none" transparent={true} visible={showListModal} onRequestClose={() => onClose(false)}>
      <TouchableOpacity onPress={() => onClose(false)} style={styles.centeredView}>
        <View style={styles.modalView}>
          <ScrollView showsVerticalScrollIndicator={false}>
            {data.map((item, index) => (
              <>
                <TouchableOpacity
                  onPress={() => {
                    onSelectItem(item);
                    onClose(false);
                  }}
                  style={{ height: 43, justifyContent: 'center' }}
                >
                  <Text style={styles.itemLabel}>{item.label}</Text>
                </TouchableOpacity>
                {index <= data.length - 2 && (
                  <View
                    style={{
                      borderBottomColor: colors.white,
                      opacity: 0.2,
                      borderWidth: 1,
                      marginHorizontal: (24 / 375) * screenWidth,
                    }}
                  />
                )}
              </>
            ))}
          </ScrollView>
        </View>
      </TouchableOpacity>
    </Modal>
  );
};

样式:

const styles = StyleSheet.create({
  centeredView: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#00000099',
  },
  modalView: {
    marginHorizontal: wp('5%'),
    marginVertical: hp('10%'),
    backgroundColor: colors.popupBlack,
    borderRadius: 20,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.25,
    shadowRadius: 3.84,
    elevation: 5,
  },
  itemLabel: {
    fontSize: wp('5%'),
    color: colors.white,
    paddingHorizontal: (24 / 375) * screenWidth,
  },
});

用法:

<ListInModal
  data={projectState?.lvApplicationTypeList}
  showListModal={showListModal}
  onClose={(bool) => setshowListModal(bool)}
  onSelectItem={(item) => onPressApplicationType(item.label)}
/>

答案 13 :(得分:0)

我已经通过下面给出的代码在 react native 0.64 中解决了这个问题

 <Modal  
     isVisible={ModalVisible}  

      onBackdropPress={()=>{setModalVisible(false)}}
       >
      .
      .
      . 
  </Modal>

答案 14 :(得分:0)

我是这样做的。

<Modal
      visible={isVisible}
      onRequestClose={() => setIsVisible(false)}
      transparent={true}
      
      >
          <Pressable style={{
              flex:1,
              backgroundColor:'transparent',
            
          }}
          onPress={()=>setIsVisible(false)}
          />
          {/* Here comes your component*/}
    </Modal>

使用 position:absoute 制作组件,以便 Pressable 可以覆盖整个背景。

答案 15 :(得分:-1)

嗨,我使用的是轻量级弹出窗口# Build the training-relevant part of the graph. labels = tf.placeholder(dtype=tf.float32, shape=(None, num_classes), name='labels') loss = tf.reduce_mean(tf.keras.backend.categorical_crossentropy(target=labels, output=model.output, from_logits=False)) train_step = tf.train.AdamOptimizer().minimize(loss) with tf.variable_scope('metrics') as scope: predictions_argmax = tf.argmax(model.output, axis=-1, output_type=tf.int64) labels_argmax = tf.argmax(labels, axis=-1, output_type=tf.int64) mean_loss_value, mean_loss_update_op = tf.metrics.mean(loss) acc_value, acc_update_op = tf.metrics.accuracy(labels=labels_argmax, predictions=predictions_argmax) local_metric_vars = tf.contrib.framework.get_variables(scope=scope, collection=tf.GraphKeys.LOCAL_VARIABLES) metrics_reset_op = tf.variables_initializer(var_list=local_metric_vars, name='metrics_reset_op') # Run the training. With BatchNorm. epochs = 3 steps_per_epoch = 1000 fetch_list = [mean_loss_value, acc_value, moving_mean, moving_variance, train_step, mean_loss_update_op, acc_update_op] + model.updates sess.run(tf.global_variables_initializer()) sess.run(tf.local_variables_initializer()) with sess.as_default(): for epoch in range(1, epochs+1): tr = trange(steps_per_epoch, file=sys.stdout) tr.set_description('Epoch {}/{}'.format(epoch, epochs)) sess.run(metrics_reset_op) for train_step in tr: b_images, b_labels = sess.run([batch_features, batch_labels]) ret = sess.run(fetches=fetch_list, feed_dict={tf.keras.backend.learning_phase(): 1, model.input: b_images, labels: b_labels}) tr.set_postfix(ordered_dict={'loss': ret[0], 'accuracy': ret[1], 'bn1 moving mean': ret[2], 'bn1 moving variance': ret[3]}) ,当您轻按弹出窗口时,它也会自动关闭。

react-native-easypopup