More than code
Welcome to the website

React Native 状态栏优化(刘海屏适配、沉浸式、启动页全屏)

前言

React Native开发中,我们通常通过StatusBar组件控制状态栏的背景色、文字颜色、显示隐藏:

<View>
       <StatusBar
         backgroundColor="blue"
         barStyle="light-content"
       />
       <Navigator
             initialRoute={{statusBarHidden: true}}
             renderScene={(route, navigator) =>
               <View>
                     <StatusBar hidden={route.statusBarHidden} />
                 ...
               </View>
         }
       />
 </View>

这也许在通常的开发中就能满足需求。在一次研究启动页全屏的时候,发现通过StatusBar设置只能在bundle文件加载后生效,在android的activity加载的时候,也就是应用启动时的瞬间,状态栏依然是存在的。这种情况下会引发的一些交错的问题:

1. 在设置启动屏,也就是在给android的activity设置背景图片来‘修复启动白屏’的时候,背景并不能全屏显示,状态栏存在,不友好

2. 如果在android代码中将状态栏设置为沉浸式,启动页全屏的问题会得到解决,但是又会出现如下问题:

  • React NativeStatusBar设为非沉浸式:在应用热启动,即应用进程没有完全杀死的情况下进入应用,应用设置的StatusBar就会失效,此时,应用出现沉浸式,头部内容上移,被状态栏覆盖。因为在这种情况下,android的activity运行后设置为沉浸式,而bundle文件此时是有缓存的,并不会重新运行。
  • React NativeStatusBar设为沉浸式:此时,需要考虑刘海屏以及不同高度的状态栏的适配,需要动态设置paddingTop的值,就要在android 4.4以下的不支持沉浸式的系统中取消paddingTop

解决方案

针对这些交错的问题,最终需要android和React Native配合,用相对简洁的方式解决

1. 在android的app/src/main/res/values文件夹下styles.xml中设置:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <!-- add code start -->
        <item name="android:windowTranslucentStatus">true</item>
        <!-- add code end -->
    </style>

</resources>

此时应用是沉浸式,如图

2. 动态设置padding:

因为项目中使用了React Navigation,就可以在App.js文件中通过screenProps向下传递状态栏的高度

// App.js
export default class App extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
        // add code start
        <RootStack screenProps={{ statusBarHeight: StatusBar.currentHeight}} />
        // add  code end
    )
  }
}

// navigator.js
let headerCon = {
  initialRouteName: 'Home',
  mode: 'card',
  headerMode: 'screen',
  navigationOptions: ({ navigation, screenProps }) => {
    return {
      headerTintColor: '#fff',
      headerTitleStyle: {
        fontWeight: 'normal',
        flex: 1,
        textAlign: 'center',
        fontSize: D(36)
      },
      headerStyle: {
         // add code start
        paddingTop: screenProps.statusBarHeight,
        height: D(88) + screenProps.statusBarHeight, // 88 为设计稿header高度
        // add code end
        elevation: 0,
        backgroundColor: '#4285F4',
        borderBottomColor: '#ccc',
        borderBottomWidth: StyleSheet.hairlineWidth
      },
      headerLeft: <HeaderButton onPress={() => { navigation.goBack() }} source={require('../res/imgs/header-back.png')} />,
      headerRight: <HeaderButton onPress={() => { Alert.alert('Alert Title', 'alertMessage') }} source={require('../res/imgs/header-info.png')} />
    }
  },
  transitionConfig: () => ({
    screenInterpolator: StackViewStyleInterpolator.forHorizontal
  })
}

export const RootStack = createStackNavigator(routes, headerCon)

效果如图
android 4.4+

android 4.1

3. 为了带来更好的兼容系统版本,需要通过判断android的版本号(小于4.4的时候)来决定是否这是padding值

React Native中,没有提供API获取android版本号,我们通过修改android代码,获取到版本号,传递给react native

在MainActivity.java 文件中加入以下代码

public class MainActivity extends ReactActivity {  
    /** 
     * Returns the name of the main component registered from JavaScript. 
     * This is used to schedule rendering of the component. 
     */  
    @Override  
    protected String getMainComponentName() {  
        return "StatusBarDemo";  
    }  

    //add code start
    @Override  
    protected ReactActivityDelegate createReactActivityDelegate() {  
        return new ReactActivityDelegate(this, getMainComponentName()) {  
            @Nullable  
            @Override  
            protected Bundle getLaunchOptions() {  
                Bundle bundle = new Bundle();  
                bundle.putInt("Android_SDK_INT", Build.VERSION.SDK_INT);  
                return bundle;  
            }  
        };  
    }  
    // add code end
}  

react native中,通过this.props.Android_SDK_INT即可拿到android的API level

关于android版本和API Level的对应关系,如下:

Platform Version API Level VERSION_CODE
Android 7.0 24 N
Android 6.0 23 M
Android 5.1 22 LOLLIPOP_MR1
Android 5.0 21 LOLLIPOP
Android 4.4W 20 KITKAT_WATCH
Android 4.4 19 KITKAT
Android 4.3 18 JELLY_BEAN_MR2
Android 4.2、4.2.2 17 JELLY_BEAN_MR1
Android 4.1、4.1.1 16 JELLY_BEAN

在代码中加入判断

// navigator.js
let headerCon = {
  initialRouteName: 'Home',
  mode: 'card',
  headerMode: 'screen',
  navigationOptions: ({ navigation, screenProps }) => {
      // add code start
     let _statusBarHeight = screenProps.statusBarHeight
     if (screenProps.androidAPILevel < 19) {
       _statusBarHeight = 0
     }
    // add code end
    return {
      headerTintColor: '#fff',
      headerTitleStyle: {
        fontWeight: 'normal',
        flex: 1,
        textAlign: 'center',
        fontSize: D(36)
      },
      headerStyle: {
        paddingTop: _statusBarHeight,
        height: D(88) + _statusBarHeight, // 88 为设计稿header高度
        elevation: 0,
        backgroundColor: '#4285F4',
        borderBottomColor: '#ccc',
        borderBottomWidth: StyleSheet.hairlineWidth
      },
      headerLeft: <HeaderButton onPress={() => { navigation.goBack() }} source={require('../res/imgs/header-back.png')} />,
      headerRight: <HeaderButton onPress={() => { Alert.alert('Alert Title', 'alertMessage') }} source={require('../res/imgs/header-info.png')} />
    }
  },
  transitionConfig: () => ({
    screenInterpolator: StackViewStyleInterpolator.forHorizontal
  })
}

export const RootStack = createStackNavigator(routes, headerCon)

android 4.4+

android 4.1

4. 设置启动屏

React Native开发中,导致启动缓慢的有两种情况,一种是android层面加载导致的,一种是React Nativejs文件加载导致的。
针对第二种,我们可以对js文件做缓存处理,这里不再详述。
针对android启动白屏的问题,我们修改styles.xml文件做如下处理:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="android:windowTranslucentStatus">true</item>
        <!-- add code start -->
        <item name="android:windowBackground">@mipmap/splash</item>
        <!-- add code end -->
    </style>

</resources>

然后将图片放在android/app/src/main/res/mipmap-hdpi目录下splash.png
这样使用背景图片解决开启app瞬间就会显示背景图片,不会有白屏的问题

现在为止,兼容了启动页的全屏显示、沉浸式状态栏、刘海屏(测试为MIUI 9 小米8)。

赞(1) 打赏
未经允许不得转载:李帅帅空间 » React Native 状态栏优化(刘海屏适配、沉浸式、启动页全屏)

评论 2

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    Magnificent items from you, man. I have bear in mind your stuff prior to and you are simply extremely fantastic.

    I actually like what you’ve obtained right here, certainly like what you’re stating
    and the way in which you say it. You make it enjoyable and you continue
    to take care of to stay it wise. I cant wait to learn much more from you.
    That is really a terrific website.

    cialis4个月前 (07-03)回复
    • Thank you

      李 帅帅4个月前 (07-03)回复

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏