内容简介:最近在做最开始的想法是自定义一个组件来代替原生的Q: 为什么
最近在做 react-native
应用Android端沉浸式状态栏时,发现通过 Statusbar.setTrranslucent(ture)
设置界面拉通状态栏之后,使用 Modal
组件的地方界面无法延伸到状态栏,导致使用 Modal
实现的弹窗背景蒙层顶部会有一个白条,看起来很不爽,在经过一番搜索之后,发现 react-native
github 上有人提这个问题,但是没有解决。因此就只有找其他方案来解决。
最开始的想法是自定义一个组件来代替原生的 Modal
组件,但是项目里面使用 Modal
的地方很多,替换起来也很麻烦。比较致命的一点是 Modal
组件的一些属性是不好被替代的。比如: onRequestClose
,在弹出 Modal
时,点击物理返回键,会回调这个方法,基本上所有使用 Modal
的地方都会用它来做关闭弹窗,新的组件需要报保留这些属性和功能。在网上搜到一篇文章 [React Native] 还我靓靓 modal 弹窗 ,借鉴它的思路,最后完美解决。
解决方案和思路
Q: 为什么 react native
提供的 Modal
组件Android平台不能延伸到状态栏?
A:因为 Modal
Android 原生用 Dialog
实现, Dialog
本身就不能衍生到 statusbar
因此我们改一下 Modal
原生的实现就好了。
**解决方案: 就是更改 Modal
组件的原生代码实现。重新提供一个 Modal
(就叫: TranslucentModal
)组件给 react native
**端。
注意的问题:
1、新的 Modal
组件和原来的modal 组件所暴露的属性和方法要完全一样,这样替换就很方便。
2、在 react-native
做统一封装,IOS平台继续使用 react-native
提供的 Modal
组件,Android平台使用 TranslucentModal
。
最终我们只需要在使用 Modal
的页面更改一下引用的就ok,真正的只需要修改一行代码。
import { Modal } from "react-native"; 复制代码
改为:
import Modal from 'react-native-translucent-modal'; 复制代码
效果图
对比图 | 使用RN原生的Modal | 使用Translucent Modal |
---|---|---|
splash | ||
pop |
具体实现
1、原生端代码更改
Modal
组件Android端的实现类为 com.facebook.react.views.modal.ReactModalHostView.java
,这个类是 public
的,因此我们就可以在我们自己的项目下创建一个新类 TranslucentModalHostView
继承自 ReactModalHostView
,修改部分实现就好了,如下:
/** * React Native Modal(Android) 延伸到状态栏 * 由于React Native 提供的 Modal 组件不能延伸到状态栏,因此,只有对原生{@link ReactModalHostView}实现修改。 */ public class TranslucentModalHostView extends ReactModalHostView { public TranslucentModalHostView(Context context) { super(context); } @Override protected void setOnShowListener(DialogInterface.OnShowListener listener) { super.setOnShowListener(listener); } @Override protected void setOnRequestCloseListener(OnRequestCloseListener listener) { super.setOnRequestCloseListener(listener); } @Override protected void setTransparent(boolean transparent) { super.setTransparent(transparent); } @Override protected void setHardwareAccelerated(boolean hardwareAccelerated) { super.setHardwareAccelerated(hardwareAccelerated); } @Override protected void setAnimationType(String animationType) { super.setAnimationType(animationType); } @Override protected void showOrUpdate() { super.showOrUpdate(); Dialog dialog = getDialog(); if (dialog != null) { setStatusBarTranslucent(dialog.getWindow(), true); setStatusBarColor(dialog.getWindow(), Color.TRANSPARENT); setStatusBarStyle(dialog.getWindow(), isDark()); } } @TargetApi(23) private boolean isDark() { Activity activity = ((ReactContext) getContext()).getCurrentActivity(); // fix activity NPE if (activity == null) { return true; } return (activity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0; } public static void setStatusBarTranslucent(Window window, boolean translucent) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { View decorView = window.getDecorView(); if (translucent) { decorView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { WindowInsets defaultInsets = v.onApplyWindowInsets(insets); return defaultInsets.replaceSystemWindowInsets( defaultInsets.getSystemWindowInsetLeft(), 0, defaultInsets.getSystemWindowInsetRight(), defaultInsets.getSystemWindowInsetBottom()); } }); } else { decorView.setOnApplyWindowInsetsListener(null); } ViewCompat.requestApplyInsets(decorView); } } public static void setStatusBarColor(final Window window, int color) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(color); } } public static void setStatusBarStyle(Window window, boolean dark) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { View decorView = window.getDecorView(); decorView.setSystemUiVisibility( dark ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0); } } } 复制代码
就这样,功能就实现了,现在我们需要把它以组件的形式提供给 react native
端,可以看一下 com.facebook.react.views.modal
用到了如下几个类:
它们的可见性都是包内访问的,因此在我们自己的包下访问不了,因此,需要把这几个类拷贝一份出来:
在 TranslucentReactModalHostManager
中换一下对应的名字就ok 了。
2、 react native
端统一封装
因为我们提供的属性要和原来的 Modal
组件保持一致,因此,我们把原来的 Modal.js
文件拷贝一份出来改一下,把 ios 端的属性和相关方法剔除掉,剩下Android 平台的属性相关就好了。最终如下,取名为 MFTranslucentModal.android.js
:
const AppContainer = require('AppContainer'); const I18nManager = require('I18nManager'); const Platform = require('Platform'); const React = require('React'); const PropTypes = require('prop-types'); const StyleSheet = require('StyleSheet'); const View = require('View'); const requireNativeComponent = require('requireNativeComponent'); const RCTModalHostView = requireNativeComponent('RCTTranslucentModalHostView', null); /** * The Modal component is a simple way to present content above an enclosing view. * * See https://facebook.github.io/react-native/docs/modal.html */ class Modal extends React.Component { static propTypes = { /** * The `animationType` prop controls how the modal animates. * * See https://facebook.github.io/react-native/docs/modal.html#animationtype */ animationType: PropTypes.oneOf(['none', 'slide', 'fade']), /** * The `transparent` prop determines whether your modal will fill the * entire view. * * See https://facebook.github.io/react-native/docs/modal.html#transparent */ transparent: PropTypes.bool, /** * The `hardwareAccelerated` prop controls whether to force hardware * acceleration for the underlying window. * * See https://facebook.github.io/react-native/docs/modal.html#hardwareaccelerated */ hardwareAccelerated: PropTypes.bool, /** * The `visible` prop determines whether your modal is visible. * * See https://facebook.github.io/react-native/docs/modal.html#visible */ visible: PropTypes.bool, /** * The `onRequestClose` callback is called when the user taps the hardware * back button on Android or the menu button on Apple TV. * * See https://facebook.github.io/react-native/docs/modal.html#onrequestclose */ onRequestClose: (Platform.isTVOS || Platform.OS === 'android') ? PropTypes.func.isRequired : PropTypes.func, /** * The `onShow` prop allows passing a function that will be called once the * modal has been shown. * * See https://facebook.github.io/react-native/docs/modal.html#onshow */ onShow: PropTypes.func, }; static defaultProps = { visible: true, hardwareAccelerated: false, }; static contextTypes = { rootTag: PropTypes.number, }; render() { if (this.props.visible === false) { return null; } const containerStyles = { backgroundColor: this.props.transparent ? 'transparent' : 'white', }; let animationType = this.props.animationType; if (!animationType) { // manually setting default prop here to keep support for the deprecated 'animated' prop animationType = 'none'; } const innerChildren = __DEV__ ? (<AppContainer rootTag={this.context.rootTag}> {this.props.children} </AppContainer>) : this.props.children; return ( <RCTModalHostView animationType={animationType} transparent={this.props.transparent} hardwareAccelerated={this.props.hardwareAccelerated} onRequestClose={this.props.onRequestClose} onShow={this.props.onShow} style={styles.modal} onStartShouldSetResponder={this._shouldSetResponder} > <View style={[styles.container, containerStyles]}> {innerChildren} </View> </RCTModalHostView> ); } // We don't want any responder events bubbling out of the modal. _shouldSetResponder = () => true } const side = I18nManager.isRTL ? 'right' : 'left'; const styles = StyleSheet.create({ modal: { position: 'absolute', }, container: { position: 'absolute', [side]: 0, top: 0, }, }); module.exports = Modal; 复制代码
ios 使用原来的 Modal
组件,添加一个 MFTranslucentModal.ios.js
文件,实现很简单,引用 react native 的 Modal
就ok , 如下:
import { Modal } from 'react-native'; export default Modal; 复制代码
最后,通过, index.js
文件统一导出:
import MFTranslucentModal from './MFTranslucentModal'; export default MFTranslucentModal; 复制代码
好了,整个封装过程就完成了。
新的 Modal
和原来的 Modal
使用完全一样,只需要更改一行代码那就是 import
的地方
import { Modal } from "react-native"; 复制代码
改为
import Modal from '@components/Modal'; 复制代码
为了方便使用,我已经这个组件开源的Github,地址为:
已经发布到npm仓库,引用到项目中直接使用就好具体请看Github 文档,最后,别忘了star 一下哟。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 弃繁就简!一行代码搞定 Python 日志
- 一行一行源码分析清楚AbstractQueuedSynchronizer
- 一行一行手敲webpack4配置
- HashMap源码全解析从一道面试题说起:请一行一行代码描述下hashmap put方法
- 幻术,一行代码实现镂空效果
- 一行代码逃逸 Safari 沙箱
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
疯狂又脆弱 坚定又柔软
朱墨 / 湖南文艺出版社 / 2018-3 / 39.80元
《疯狂又脆弱 坚定又柔软》是朱墨的一部作品集,介绍了作者考研到北京,工作在华谊,以及留学去英国的经历,在这短短几年中她一路升职加薪,25岁升任华谊宣传总监,27岁赚到人生的第一笔100万,30岁却毅然离职去英国留学,在表面的光鲜亮丽之下,她也曾付出过外人所不知道的心血和努力。她的人生告诉我们,每一个身居高位或者肆意潇洒的人,都曾为梦想疯狂地倾尽全力,而那些心怀梦想的人也总是怀揣一颗坚定又柔软的内心......一起来看看 《疯狂又脆弱 坚定又柔软》 这本书的介绍吧!
JSON 在线解析
在线 JSON 格式化工具
Markdown 在线编辑器
Markdown 在线编辑器