React Navigation 构建 Android 和 iOS 统一的 UI

编辑于2019年01月23日

React Navigation 是 React Native 项目常用的路由和导航库,通过它我们可以很轻松的构建多个页面并完成页面的跳转。不过,因为 iOS 和 Android 在设计风格上的差异,React Navigation 在不同平台上的表现也不同。例如,切换 StackNavigator 页面时,iOS 是从右侧滑入,而 Android 是从底部滑入。
在 React Native 项目中,有时我们想要构建 iOS 和 Android 统一的 UI,这时我们就需要对 React Navigation 的默认配置进行一些修改。本文以 iOS 风格为主,介绍如何构建在 Android 上表现相同的 UI。

主要的差异

开始之前,先看一下 iOS 和 Android 之间有哪些差异,这样我们才清楚自己要做什么。

状态栏

iOS 中 App 是全屏模式,状态栏不占据布局空间。

ios_statusbar

Android 中状态栏默认是占据布局空间的。

android_statusbar

在 iOS 中,Header 的高度是 44,标题居中,底部有边框,返回按钮图标是“<”。

ios_header

在 Android 中,Header 的高度是 56,标题居左,底部有阴影,返回按钮图标是“←”。

android_header

同时在标题的字体上也存在差异。

页面切换效果

iOS 是从右侧滑入划出,而 Android 是从底部的淡入淡出。

transition

同时,我们还可以注意到在切换时,iOS 的 Header 是固定在顶部的,而 Android 是随页面一起切换的(Android 截图不便,这里就没有贴出)。

统一 UI

在了解完差异以后,现在来看看怎么抹平这些差异来实现统一的 UI(iOS 风格)。

状态栏

此前对状态栏这一块理解还不够深,很多东西没有说清楚,所以我又重新写了一篇博客全面的介绍了关于状态栏的问题。

在 iOS 中页面是全屏的,状态栏不占空间,页面内容会上移到状态栏下面(这是 iOS 中有 SafeAreaView 的原因之一),而Android 中默认状态栏是占空间的。通常这没有什么问题,但是当我们需要设置全屏背景或者页面顶部有背景图片时,Android 上的体验就较差了。

幸好,在 Android 4.4 以后,通过 FLAG_TRANSLUCENT_STATUS 属性可以将状态栏设置成不占据布局空间的透明样式。我们可以在 Android 代码中设置:

activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

也可以在 theme 中设置 windowTranslucentStatus

<!-- android/app/src/main/res/values/styles.xml -->

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowTranslucentStatus">true</item>
</style>

还可以通过 StatusBartranslucent 属性进行设置。

<StatusBar translucent />

设置完以后效果如下图。

android_statusbar_1

从图中可以看出,状态栏现在不占据空间,内容上移导致状态栏和标题栏重叠了。为了让标题栏回到原来的位置,我们需要增加 Header 的高度,并且添加一个上边距,大小为状态栏的高度:

static navigationOptions = {
  title: 'PageB',
  headerStyle: Platform.OS === 'android' ? {
    paddingTop: StatusBar.currentHeight,
    height: StatusBar.currentHeight + 56,
  } : {}
}

同时,还可以看到状态栏并不是完全透明的,而是有一点浅灰色,这时我们就需要使用 Android 5.0 以后的 API 来将状态栏设置为透明。

activity.getWindow().setStatusBarColor(Color.TRANSPARENT);

还可以通过 StatusBarbackgroundColor 属性进行设置。

<StatusBar backgroundColor="transparent" />

网上还搜索到可以通过 xml 的方式进行设置,但是我几番尝试后发现没有效果,不知道具体什么原因。

<item name="android:statusBarColor">@android:color/transparent</item>

Header 和过滤效果我们都可以通过 createStackNavigator 暴露出来的属性来进行配置。

{
  defaultNavigationOptions: {
    headerTitleStyle: {
      fontSize: 17,
    }
  },
  headerMode: 'float', // 公用一个顶部 Header,页面切换时显示动画
  headerLayoutPreset: 'center' // 标题居中
}

参考链接