前言
在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 Native
中StatusBar
设为非沉浸式:在应用热启动,即应用进程没有完全杀死的情况下进入应用,应用设置的StatusBar
就会失效,此时,应用出现沉浸式,头部内容上移,被状态栏覆盖。因为在这种情况下,android的activity运行后设置为沉浸式,而bundle文件此时是有缓存的,并不会重新运行。 - 在
React Native
中StatusBar
设为沉浸式:此时,需要考虑刘海屏以及不同高度的状态栏的适配,需要动态设置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 Native
js文件加载导致的。
针对第二种,我们可以对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)。
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.
Thank you