背景
在应用 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>
)
附,相干源码