起点RN Demo安卓迁移问题汇总

1. 三方库(调用原生)的使用

这里的三方库特指需要调用原生的库,目前项目里用到了react-native-share(分享)、react-native-linear-gradient(渐变)、react-native-splash-screen(闪屏)、react-native-svg(svg)。
正常我们使用react-native link命令,原生依赖就成功地链接到你的iOS/Android项目了,但出于原生代码可能被改写的情况,需要在原生代码中确认一下引入情况:

2. lineHeight

在迁移过程发现几个页面都出现一下错误提示:

Error while updating property 'lineHeight' in shadow node of type:RCTText

查文档给出的解释:

lineHeight must be an integer, numbers with decimals are not considered as valid values.

原来安卓上lineHeight必须是整数,由于我们的项目中用了p2dp进行适配,会有把整数转成了小数的情况,解决方法就是p2dp的结果进行向上取整。

3. 字体引入

安卓上要使用自定义的字体,必须要把字体文件放在[project root]/android/app/src/main/assets/fonts/目录下才能生效。

4. StatusBar

StatusBar是一个值得被重视的差异,它会影响到头部导航的高度样式问题,甚至是整个页面的高度问题。

4.1 StatusBar高度

  • 4.1.1 Android 手机状态栏
    (1)当状态栏呈现在 Andorid 手机屏幕顶部时,它会占用顶部这个空间,我们只能使用剩下的屏幕空间。也就是说如果从第 0 行开始放置组件时,组件会紧贴着状态栏的下边沿显示;
    (2)要想知道实际可用的屏幕高度,可以通过手机屏幕的高度减去状态栏高度得到,安卓状态栏的高度可通过StatusBar.currentHeight获取。
  • 4.1.2 iOS 手机状态栏
    (1)在 iOS 平台上,取得的屏幕高度就是实际可使用的高度;
    (2)如果从第 0 行开始排列组件时,组件会紧贴着手机屏幕的最上沿显示。如果状态栏没有被隐藏,它将覆盖在第 0 行组件的上方;
    (3)如果不想设置状态栏隐藏,则应当空出状态栏的显示区域。但可以为这个区域设置背景色,以使整个界面风格统一。

4.2 StatusBar样式

Default status bar style (dark for iOS, light for Android)

在Android上默认的样式是白色,而IOS是黑色,所以当我们在跨平台设置StatusBar样式时,避免使用defult,应该直接使用light-contentdark-content

4.3 StatusBar背景色

Android上可通过backgroundColor设置背景色,而IOS无此属性,需要通过手动设置20dp高度的颜色作为背景色。另外项目中有个特殊需求,需要StatusBar的颜色是渐变的,这时安卓上需要让背景色透明,设置同等高度的渐变色来替代。

5. 闪屏、广告页面

因为闪屏涉及端代码的编写,自然需要重新开发。
广告页面则有两个思路:可以在端开发、也可以放在RN:

  • 在端的好处在于RN的首页是"真首页",在启动应用显示闪屏的时候,为预加载首页提供了时间,让首页出现的时间大大提升;缺点就是不够灵活,如果将广告图换一个表现形式,需要重新发包。
  • 在RN的好处自然就是灵活,可以频繁的变换广告形式也无需更新App,另外如果是复杂的广告形式,对于前端开发人员,开发端代码的陈本要远高于开发RN;缺点就是RN的首页变成了广告页,而不是我们想要的首页,这样启动图无法争取到首页的访问的时间,在首页呈现的时间上肯定比上面的方法慢一些。

6. 轮播组件

这个问题涉及到了第三方组件的兼容性。
之前在IOS上用的Carousel组件在Android会有一些异常的表现。解决方案有两种:

    1. 修改组件源码,修复异常;
    1. 用一个兼容性没问题的同类组件替换

在解决过程中两种办法都尝试过,都可行。从这里也获取一个经验,之后在选择三方库的时候,尽量选择更新时间近、star数量高、仍在维护、大厂出品的。

7. 监听物理回退

由于安卓机子有一个物理回退键,如果在RN项目中不对其处理,则点击物理回退键就会关闭App,而我们的预期是希望点击后回退上一页。

首页我们要知道,点击物理回退,其实会调用端的onBackPressed方法,所以我们需要判断如果是RN页面,应该让RN代码自行决定处理方式,当然这层逻辑其实初始化项目就做好了:

    @Override
    public void onBackPressed() {
        //super.onBackPressed();

        if(mReactInstanceManager != null){
            mReactInstanceManager.onBackPressed();
        }else{
            super.onBackPressed();
        }
    }

这样我们只需要在RN项目里监听这个回退,然后处理自己想要的逻辑就行了,代码大致如下:

    componentDidMount() {
        if (Platform.OS === 'android') {
            BackHandler.addEventListener('hardwareBackPress', this._handleBack.bind(this));
        }
    }

    componentWillUnmount() {
        if (Platform.OS === 'android') {
            BackHandler.removeEventListener('hardwareBackPress', this._handleBack.bind(this));
        }
    }

只需要在程序入口做一次监听,后续页面无需处理。

8. Modal

Android平台Modal组件中的onRequestClose是一个必传参数,IOS则不需要。
可以在Modal的文档上可以看到关于onRequestClose参数的描述:

On the Android platform, this is a required function.

至于为什么只有安卓平台需要,我猜大概率和物理回退的相关处理有关系。

9. 自定义字体的高度问题

目前项目中引入了两个外部字体:思源宋和阅文定制的数字字体。
引入字体会有一定高度的上下pandding,样式处理上也有别于常规字体,需要设置lineHeightheight更好的定位位置。

10. Text样式之ellipsizeMode属性

Text样式中的ellipsizeMode属性 , 用来配合numberOfLines 显示不完全省略的位置, 可选值'head', 'middle','tail','clip',而其中'clip'只能在IOS中使用,安卓平台会报错。

11. popover组件

这个问题同问题6,一样涉及到了第三方组件的兼容性。
其中比较核心的问题是安卓平台上不支持shadow阴影属性的设置,这个问题在下一条会提到。
另外这个库的开发时间比较早且已经无人维护,所以类似这样的库以后应该规避。
至于最终的解决方案,其实是加了一层蒙层来替代阴影的效果。

12. 阴影属性

在IOS上可以通过如下设置阴影:

    shadowOffset: {width: 0, height: 0},
    shadowColor: 'black',
    shadowOpacity: 1,
    shadowRadius: 5

而Android上并不支持shadow相关的属性,只提供了一个elevation属性,使用方法如下:

    backgroundColor: 'black',
    elevation: 20,

elevation顾名思义就是 “仰角”,通过为视图增加 “仰角” 方式来提供阴影,仰角越大,阴影越大。
elevation的设置比较局限,设置无法设置offset。如果需要更好的效果,还是需要通过其它方式去实现,svg、渐变等,当然也有这样库提供了解决方案,像react-native-shadowreact-native-cardview都是值得参考的。

13. 触摸点击

目前关于触摸点击有以下几种方式:

  • TouchableHighlight

• What it does: Darkens or lightens the background of the element when pressed.
• When to use it: On iOS for touchable elements or buttons that have a solid shape or background, and on ListView items.

  • TouchableOpacity

• What it does: Lightens the opacity of the entire element when pressed.
• When to use it: On iOS for touchable elements that are standalone text or icons with no backgroundcolor.

  • TouchableNativeFeedback

• What it does: Adds a ripple effect to the background when pressed.
• When to use it: On Android for almost all touchable elements.

安卓平台推荐用TouchableNativeFeedback

14. TextInput

TextInput在安卓平台默认有下划线且框内文字样式也有问题,需要做如下兼容处理:

    underlineColorAndroid = "transparent"  //android需要设置下划线为透明才能去掉下划线
    style={{padding:0}}// 矫正样式

15. ScrollView

我们观察Android和IOS默认的下拉刷新表现形式是不一样的,这是因为安卓上的ScrollView没有IOS的弹性bounces,这也使得我们通过onScrollonScrollEndDrag获取X轴、Y轴距离做的一些判断会有所区别。

16. react-native-scrollable-tab-view组件卡顿

这个组件在切换两个数据量稍大的Tab时,就会有明显的卡顿,开始以为是性能问题导致动画卡顿,后来debug发现是组件在切换回已经render的view,还做了一次render,导致阻塞了动画的执行,解决办法是通过shouldComponentUpdate判断是否有必要render,当然也可以使用PureComponent来实现。

推荐阅读更多精彩内容