【安卓】自定义React Native Modal,支持全屏弹框

背景

在使用 React Native(以下简称 RN ) 开发移动App时,会碰到很多弹窗的场景,虽然 RN自带了一个 Modal 组件可以实现这一效果,但是由于Android和iOS平台的差异性,使得使用同一个组件开发出来的效果会略有差异。比如,Modal组件在iOS平台,弹框是全屏的,但是在Android平台却不是,会有状态栏,如下效果。
【安卓】自定义React Native Modal,支持全屏弹框
之所以这样,是因为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,支持全屏弹框 的全部内容, 来源链接: utcz.com/a/104050.html

回到顶部