自定义React Native Modal,支持全屏弹框
2020/12/1 5:25:30
本文主要是介绍自定义React Native Modal,支持全屏弹框,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
背景
在使用 React Native(以下简称 RN ) 开发移动App时,会碰到很多弹窗的场景,虽然 RN自带了一个 Modal 组件可以实现这一效果,但是由于Android和iOS平台的差异性,使得使用同一个组件开发出来的效果会略有差异。比如,Modal组件在iOS平台,弹框是全屏的,但是在Android平台却不是,会有状态栏,如下效果。
之所以这样,是因为Android 端的Modal 控件使用的Dialog,内容无法从状态栏处开始布局。而iOS是基于 Window 的,所以是覆盖在视图上面的。如果要让双端的样式一样,那么需要对Android进行特殊处理。
由于RN的Modal 组件在Android中是使用Dialog实现的,所以如果要实现一个全屏的弹框,那么就需要自定义一个全屏展示的Dialog。
1,自定义Dialog
首先,我们新建一个继承自Dialog的自定义组件FullModal,代码如下:
package com.cgv.cn.movie.modal; import android.app.Dialog; import android.content.Context; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.StyleRes; public class FullModal extends Dialog { private boolean isDarkMode; private View rootView; public void setDarkMode(boolean isDarkMode) { this.isDarkMode = isDarkMode; } public FullModal(@NonNull Context context, @StyleRes int themeResId) { super(context, themeResId); } @Override public void setContentView(@NonNull View view) { super.setContentView(view); this.rootView = view; } @Override public void show() { super.show(); StatusBarUtil.setTransparent(getWindow()); if (isDarkMode) { StatusBarUtil.setDarkMode(getWindow()); } else { StatusBarUtil.setLightMode(getWindow()); } AndroidWorkaround.assistView(rootView, getWindow()); } }
在上面的代码中,StatusBarUtil.setTransparent(getWindow())
方法的主要作用就是将状态栏背景透明,并且让布局内容可以从 Android 状态栏开始。然后我们看一下setTransparent()方法的实现。
@TargetApi(Build.VERSION_CODES.KITKAT) private static void transparentStatusBar(Window window) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { View decorView = window.getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); window.setStatusBarColor(Color.TRANSPARENT); } else { window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } }
需要说明的是,transparentStatusBar()方法只在 Android 4.4 以上才会有效果,Android 4.4之前要实现沉浸式状态栏需要使用其他的方式(比如反射)。不过现在都已经是9012年了, Android 4.4版本的应该很少了吧。
2, RN封装调用
自定义的原生组件开发完成之后,接下来就是按照RN和原生交互的规则进行RN的封装。首先,新建一个FullModalManager类,该类的主要作用就是RN的Android和Js的通信桥梁,代码如下。
public class FullModalManager extends ViewGroupManager<FullModalView> { @Override public String getName() { return "RCTFullScreenModalHostView"; } public enum Events { ON_SHOW("onFullScreenShow"), ON_REQUEST_CLOSE("onFullScreenRequstClose"); private final String mName; Events(final String name) { mName = name; } @Override public String toString() { return mName; } } @Override @Nullable public Map getExportedCustomDirectEventTypeConstants() { MapBuilder.Builder builder = MapBuilder.builder(); for (Events event : Events.values()) { builder.put(event.toString(), MapBuilder.of("registrationName", event.toString())); } return builder.build(); } @Override protected FullModalView createViewInstance(ThemedReactContext reactContext) { final FullModalView view = new FullModalView(reactContext); final RCTEventEmitter mEventEmitter = reactContext.getJSModule(RCTEventEmitter.class); view.setOnRequestCloseListener(new FullModalView.OnRequestCloseListener() { @Override public void onRequestClose(DialogInterface dialog) { mEventEmitter.receiveEvent(view.getId(), Events.ON_REQUEST_CLOSE.toString(), null); } }); view.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { mEventEmitter.receiveEvent(view.getId(), Events.ON_SHOW.toString(), null); } }); return view; } @Override public LayoutShadowNode createShadowNodeInstance() { return new FullScreenModalHostShadowNode(); } @Override public Class<? extends LayoutShadowNode> getShadowNodeClass() { return FullScreenModalHostShadowNode.class; } @Override public void onDropViewInstance(FullModalView view) { super.onDropViewInstance(view); view.onDropInstance(); } @ReactProp(name = "autoKeyboard") public void setAutoKeyboard(FullModalView view, boolean autoKeyboard) { view.setAutoKeyboard(autoKeyboard); } @ReactProp(name = "isDarkMode") public void setDarkMode(FullModalView view, boolean isDarkMode) { view.setDarkMode(isDarkMode); } @ReactProp(name = "animationType") public void setAnimationType(FullModalView view, String animationType) { view.setAnimationType(animationType); } @ReactProp(name = "transparent") public void setTransparent(FullModalView view, boolean transparent) { view.setTransparent(transparent); } @ReactProp(name = "hardwareAccelerated") public void setHardwareAccelerated(FullModalView view, boolean hardwareAccelerated) { view.setHardwareAccelerated(hardwareAccelerated); } @Override protected void onAfterUpdateTransaction(FullModalView view) { super.onAfterUpdateTransaction(view); view.showOrUpdate(); } }
FullModalManager类最重要的createViewInstance()方法。并且,在事件通信中,RN的Modal 已经存在了onShow()和 onRequestClose()回调,但是这里不能再使用这两个命名,所以这里改成了 onFullScreenShow 和 onFullScreenRequstClose,但是在Js端还是重新命名成 onShow 和 onRequestClose ,所以在使用过程中还是没有任何变化。
在RN的Js部分,我们只需要处理 Android 的实现即可,而对于iOS部分则不需要处理。为了方便在RN代码中进行引用,我们可以参考RN自定义组件的方式新建FullModal.android.js和FullModal.ios.js两个文件,其中FullModal.android.js的源码如下。
const FullScreenModal = requireNativeComponent('RCTFullScreenModalHostView', null); export default class FullModalViewAndroid extends Component { _shouldSetResponder = () => { return true; } static propTypes = { isDarkMode: PropTypes.bool, // false 表示白底黑字,true 表示黑底白字 autoKeyboard: PropTypes.bool, // 未知原因的坑,modal中的edittext自动弹起键盘要设置这个参数为true }; render() { if (this.props.visible === false) { return null; } const containerStyles = { backgroundColor: this.props.transparent ? 'transparent' : 'white', }; return ( <FullScreenModal style={{position: 'absolute'}} {...this.props} onStartShouldSetResponder={this._shouldSetResponder} onFullScreenShow={() => this.props.onShow && this.props.onShow()} onFullScreenRequstClose={() => this.props.onRequestClose && this.props.onRequestClose()}> <View style={[{position: 'absolute', left: 0, top: 0}, containerStyles]}> {this.props.children} </View> </FullScreenModal> ); } }
接下来,我们就可以在业务代码中进行使用了,如下所示。
const ModalView = tools.isIos ? Modal : FullModal return ( <ModalView transparent={false} visible={targetShow} onRequestClose={() => { }}> <View style={{ flex: 1 }}> ...//省略其他代码 </View> </ModalView> )
附,相关源码
这篇关于自定义React Native Modal,支持全屏弹框的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-24Vite多环境配置学习:新手入门教程
- 2024-11-23实现OSS直传,前端怎么实现?-icode9专业技术文章分享
- 2024-11-22在 HTML 中怎么实现当鼠标光标悬停在按钮上时显示提示文案?-icode9专业技术文章分享
- 2024-11-22html 自带属性有哪些?-icode9专业技术文章分享
- 2024-11-21Sass教程:新手入门及初级技巧
- 2024-11-21Sass学习:初学者必备的简单教程
- 2024-11-21Elmentplus入门:新手必看指南
- 2024-11-21Sass入门:初学者的简单教程
- 2024-11-21前端页面设计教程:新手入门指南
- 2024-11-21Elmentplus教程:初学者必备指南