beefe / react-native-platform-difference

React Native 双平台差异及解决方法

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

React Native 重大变更

入口文件改为单一的index.js

此版本之前iOS和Android的入口文件是分开的,分别是index.ios.js和index.android.js

Android端移除了createJSModules

若使用的组件中含有对createJSModules的@Override将会报错

不再支持Xcode 7.x

目前已知的7.x中的报错:yourProject/node_modules/react-native/React/Views/RCTTabBarItem.m:115:18: Property 'badgeColor' not found on object of type 'UITabBarItem *'

rnpm合并进了react-native项目

以前: 需要npm install rnpm -g,然后才能rnpm link

现在: 直接react-native link

安卓端程序入口文件变化

以前:程序入口文件为MainActivity.java

现在:分离成了程序入口文件MainApplication.java和主视图文件MainActivity.java

影响:添加原生组件的方式有变化,不过有了rnpm这些都是浮云了(然而也是到了v0.29.2才能正常添加)。以前添加启动图的方式也会受到影响

由原来的react-native组件一锅端,变成了更接近react标准的写法

以前:

    import React, { Component, View } from 'react-native';

现在:

    import React, { Component } from 'react';
    import { View } from 'react-native';

影响:赶紧升级组件去吧

React Native 双平台差异及解决方法(0.20)

标签及属性差异

1. TextInput

  • iOS下的textAlign取值 auto left right center justify
  • android下的textAlign取值为 start center end
    安卓平台有黑色边框和选中黄框,可通过设置underlineColorAndroid='rgba(0,0,0,0)'去掉
    安卓平台无清除按钮,可以使用 react-native-textinput 来兼容双平台
  • android下默认有大概10dppaddingLeftpaddingRight
  • android下的placeholder默认颜色大概在#777,iOS下默认颜色大概在#ccc

2. Image

  • iOS下resizeMode可以写在行间属性,也可以写在style,后者覆盖前者
  • android下resizeMode只可以写在行间,写在style无效
    //android 
    <Image resizeMode={'stretch'}></Image>
    //ios, style中的cover会覆盖掉stretch
    <Image resizeMode={'stretch'} style={{'resizeMode': 'cover'}}></Image>

3. Text

  • iOS下最主要的paddinglineHeight的 style 属性都正常支持
  • android下paddinglineHeight无效,包括单独支持 android 的textAlignVertical enum('auto', 'top', 'bottom', 'center') 也不支持,解决办法就是 padding 改用 margin,或 padding 改用父节点 paddinglineHeight 改用 marginTop 负值
    //双平台

    let isAndroid = Platform.OS === 'android';

    let styleTextLine1 = {
    	fontSize: 12,
    	marginBottom: 20,
	};
	let styleTextLine2 = {
		fontSize: 50,
		height: 50,
		lineHeight: 50, // ios
		marginTop: isAndroid ? -15 : 0,
	};

    <View style={{height: 100, justifyContent: 'center', alignItems: 'center'}}>
    	<Text style={styleTextLine1}>第一行,要求与第二行有间隔 20,字体大小 12</Text>
    	<Text style={styleTextLine2}>第二行,要求字体大小 50,字体上下居中,高度 50</Text>
    </View>

4. style position: 'absolute'

  • iOS下正常
  • android下,position: 'absolute' 超过父节点高宽部分,会隐藏掉。解决办法:当需要用position: 'absolute'的时候,恰巧要求:子节点定位超出父节点高或宽,放弃使用,改用别的布局,或者将子节点放到于父节点同级,再定位。

5. style borderRadius

  • iOS下正常
  • Android下,不支持类似borderTopLeftRadius这种写法

6. style lineHeight

  • 两端都不支持类似web height=lineHeight的方式实现居中
  • iOS会显示在lineHeight的最底端
  • Android会显示在lineHeight的最顶端 lineHeight

API差异

1. api NativeMethodsMixin static measure(callback: MeasureOnSuccessCallback)

  • iOS下正常
  • android下,回调函数获取不到视图的尺寸 xywidthheigthpageXpageY 等值。可以考虑用 onLayout 属性来代替,需要注意的是,需要计算 layout 的视图,载入之后,防止内部不必要的重绘引起的尺寸变化,基本上保证 state 改变后,视图的尺寸即可预见。

常见问题:

1. 返回事件的处理

  • iOS如果使用NavigatorIOS,并且设置了navigationBarHidden={true}(隐藏NavigationBar),将会导致右滑返回手势失效,解决方法:使用Navigator代替,但需要自己设置切场动画。
  • android物理返回键,需要在每个Navigator的源头使用BackAndroid做监听,如果this.navigator.state.routeStack.length === 1,则可以认为当前处于根view,可以退出app。否则将navigator往回退1即可。
    //android
    import React, {Navigator, BackAndroid} from 'react-native';
    //拓展类似于NavigatorIOS的popN
    Navigator.prototype.popN = function(n) {
        let routeStack = this.state.routeStack;
        let index = routeStack.length - n - 1;
        if (routeStack[index]) {
            this.popToRoute(routeStack[index]);
        }
    };
    class MyApp extends React.Component {
        ...
        componentDidMount(){
            BackAndroid.addEventListener('hardwareBackPress', () => {
                //不在根view, 往回退1,阻止退出
                if(this._navigator.state.routeStack.length > 1){
                    this._navigator.popN(1);
                    return true;
                }
                //在根view,退出(或再次点击退出)
                else{
                    return false;
                }
            });
        }
        render(){
            <Navigator
                ref={(navigator) => {
                    if(navigator !== this._navigator){
                        this._navigator = navigator;
                    }
                }}
            />
        }
    }

2. 有关屏幕的高度Dimensions.get('window').height

  • 两个平台都是整个屏幕的高度(包含statusBar, 安卓端不包含虚拟按键)

  • iOS平台的布局是从statusBar的顶端开始

  • android平台的布局是从statusBar的底端开始(设置translucent: true后也从statusBar顶端开始)

  • 如果设置view的高度是Dimensions.get('window').height,然后设置position: 'absolute', top: 0,会发现android平台view的底端被遮住了一小部分,这一小部分正好就是android平台statusBar的高度

  • 安卓端statusBar通常是25dp,虚拟按键通常是48dp

  • 解决方法有四:

    • 设置view的高度是整屏的高度减去statusBar的高度
    • 设置top值为负的statusBar的高度
    • 设置bottom: -Dimensions.get('window').height 替代top: 0
    • 在每一个Navigator的入口设置<StatusBar translucent={true}/>
  • 大坑: 有些手机通过上述方法获取到的屏幕高度竟然包含了虚拟按键的高度,如魅族pro4,只能引入react-native-device-info这个库来hack一下了

3. Android过门动画卡顿

  • 从测试结果来看是网络请求和过门动作同时进行导致,将网络请求延迟到过门动画结束后即可解决。深层原因?

4. Android启动图launcher

  • 修改MainActivity的父类ReactActivity,在加载RN的view的同时在该view上面覆盖一个原生的view,用来显示启动图,并且设置N秒后隐藏或等待拿到首页数据接口后隐藏

5. 支持程度问题

  • iOS支持7.0及以上
  • Android支持API 16+ (Android 4.1) 因为使用了Choreographer
  • 友盟2016-02数据:
    • iOS7.0 以下已不计入主流版本(1%以下),和其他非主流版本共占4.92%
    • Android4.0 还有2.16%,其他非主流版本共占7.47%

6. Navigator注意事项

  • 默认背景透明,切场的时候会透底,需要给每个navigator设置背景色

About

React Native 双平台差异及解决方法