天天看點

android自定義華為彈框簡書,自定義React Native Modal,支援全屏彈框

背景

在使用 React Native(以下簡稱 RN ) 開發移動App時,會碰到很多彈窗的場景,雖然 RN自帶了一個 Modal 元件可以實作這一效果,但是由于Android和iOS平台的差異性,使得使用同一個元件開發出來的效果會略有差異。比如,Modal元件在iOS平台,彈框是全屏的,但是在Android平台卻不是,會有狀态欄,如下效果。

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 { @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 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 ( 'absolute'}} {...this.props} onStartShouldSetResponder={this._shouldSetResponder} onFullScreenShow={() => this.props.onShow && this.props.onShow()} onFullScreenRequstClose={() => this.props.onRequestClose && this.props.onRequestClose()}> 'absolute', left: 0, top: 0}, containerStyles]}> {this.props.children} );

}

}

接下來,我們就可以在業務代碼中進行使用了,如下所示。

const ModalView = tools.isIos ? Modal : FullModal

return ( false} visible={targetShow} onRequestClose={() => { }}> 1 }}> ...//省略其他代碼

)

文章來源: segmentfault.com,作者:xiangzhihong,版權歸原作者所有,如需轉載,請聯系作者。

原文連結:segmentfault.com/a/1190000038331458