在使用 react Native(以下简称 RN ) 开发移动App时,会碰到很多弹窗的场景,虽然 RN自带了一个 Modal 组件可以实现这一效果,但是由于Android和iOS平台的差异性,使得使用同一个组件开发出来的效果会略有差异。比如,Modal组件在iOS平台,弹框是全屏的,但是在Android平台却不是,会有状态栏,如下效果。
之所以这样,是因为Android 端的Modal 控件使用的Dialog,内容无法从状态栏处开始布局。而iOS是基于 Window 的,所以是覆盖在视图上面的。如果要让双端的样式一样,那么需要对Android进行特殊处理。
由于RN的Modal 组件在Android中是使用Dialog实现的,所以如果要实现一个全屏的弹框,那么就需要自定义一个全屏展示的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版本的应该很少了吧。
自定义的原生组件开发完成之后,接下来就是按照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上是怎么写样式的吧,和传统的web不一样的是,在react-native上面是没有css代码,不过得益于Yoga,我们可以在客户端上像写css一样的去书写我们的样式。
初始化 wkwebview,设置 message handler,native 端注册了 testecho 的messageHandler,实现 WKScriptMessageHandler协议,执行JS 代码 ,所以 JS 可以通过 window.webkit.messageHandlers.testecho.postMessage 来回调客户端,和文档中说的一样
因为js语言的特性,使用node开发程序的时候经常会遇到异步处理的问题。对于之前专长App开发的我来说,会纠结node中实现客户端API请求的“最佳实践”。下面以OAuth2.0为场景,需要处理的流程:获取access token、使用获取到的token,发起API请求、处理API数据
GraphQL是一个API查询语言,他可以将使用PostgreSQL写的server代码自动生成Query或者Mutation,非常的方便。而Apollo Client就是一个强大的JavaScript GraphQL客户端。对于cache,在Apollo Client中有着强大的管理策略。
React Native 是 Facebook 2015年开源的 Javascript 框架,旨在使用 Javascript 高效开发手机端 App。根据大众的需求,我们列出了一个有用的React-Native UI库列表,可以帮助你更好地入门React Native。
在React Native 中由于业务的需要, 我们往往要在诸多的页面间,组件之间做一些参数的传递与管理, 在这里我总结了几大经过验证,稳定好用的方式给大家
随着 H5 标准的发布以及推广,使得移动应用的开发也受到了很大影响,出于效率、成本等原因,移动应用的开发不再完全依赖于 “原生”。近日越发火热的混合应用(Hybrid App)介于 Web 应用和原生应用之间
react-native打包安卓apk的时候,报错Cannot get property packageName on null object,完全没有头绪,研究了半天才发现竟然是因为package.json里面scripts自定了命令导致的,无法理解为何会影响安卓打包并且报错packageName null
之前写的项目都是人家编写好的脚手架,里面包含项目所需的环境文件,但由于有些东西用不到打包增加软件体积,所以自己从头搭建个环境。是基于 Native Base + react-navigation + Redux 的 React Native 脚手架,现在项目环境如下
Flutter作为备受关注的跨平台的开发框架,长远来看,前景肯定是比较好的,在其基础组件还未完善与成熟之前,能够高效的复用现有的native组件,是比较合适的方案。官方提供了Plugin的方式,允许将一个成熟的native组件(比如mapview)
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!