当transparent
选项为true
时点击叠加层是否可以关闭react native modal?文档没有提供任何相关信息。有可能吗?
答案 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)
如果您使用诸如mobx
或redux
之类的商店解决方案,
您只需解决商店中的标志即可。
下面是父母的代码,
<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