Android 动态切换全屏横屏

疫情概要

国内疫情

截至4月25日24时,据31个省(自治区、直辖市)和新疆生产建设兵团报告,现有确诊病例801例(其中重症病例51例),累计治愈出院病例77394例,累计死亡病例4632例,累计报告确诊病例82827例,现有疑似病例12例。累计追踪到密切接触者729885人,尚在医学观察的密切接触者8308人。

海外疫情

经济日报-中国经济网北京4月26日讯(记者 朱晓航)截至4月26日上午11点,全球新冠肺炎确诊病例逾291万例,中国以外确诊病例超283万例,共31国累计确诊病例过万,其中美国960651例,西班牙223759例,意大利195351例。

过去24小时,中国以外新冠肺炎病例新增超8.4万例,其中美国今日新增3.3万多(连续第33天新增过万),俄罗斯5900多,英国4900多,西班牙3900多。

目前全球共报告新冠肺死亡病例超20万例,中国以外死亡病例逾19.8万例,其中美国死亡54256例,意大利死亡26384例,西班牙死亡22902例。

前言

最近几天的开发强度终于开始缓解下来了,小编才算是可以把之前later了好久的一个bug拿出来好好折腾折腾了。

本篇的主角bug全称:部分设备切换横屏后系统状态栏和导航栏无法隐藏,连续横竖屏切换多次后也会出现横屏全屏无法隐藏系统状态栏和导航栏。

本篇就将该问题的最终解决方案做个总结,接下来就直接进入主题。

问题版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);

if (ActivityInfo.SCREEN_ORIENTATION_USER == newConfig.orientation) { //横屏
hideSystemUI();
} else { //竖屏
showSystemUI();
}
}

/**
* 隐藏系统状态栏和导航栏
*/
private void hideSystemUI() {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE
// Set the content to appear under the system bars so that the
// content doesn't resize when the system bars hide and show.
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
// Hide the nav bar and status bar
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN);
}

/**
* 显示系统状态栏和导航栏
*/
private void showSystemUI() {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_VISIBLE);
}

改进处理版本(BaseAppCompatActivity)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

private Handler mHandler = new Handler();

/**
* 隐藏系统状态栏和导航栏
*/
private Runnable hideSystemUIAction = this::hideSystemUI;

@Override
protected void onResume() {
super.onResume();
XLog.e(TAG, "onResume");
View decorView = getWindow().getDecorView();
//注册监听器,以便让应用获得系统界面可见性更改的通知
decorView.setOnSystemUiVisibilityChangeListener(this::onSystemUiVisibilityChange);
}

@Override
protected void onPause() {
super.onPause();
XLog.e(TAG, "onPause");
View decorView = getWindow().getDecorView();
//取消注册监听器
decorView.setOnSystemUiVisibilityChangeListener(null);
}
/**
* 当前activity所属的Window得到/失去焦点时会回调该方法
*
* @param hasFocus
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
boolean orientationLandscape = isOrientationLandscape(getResources());
Log.w(TAG, String.format("onWindowFocusChanged: %s isLandscape:%s", hasFocus, orientationLandscape));
if (hasFocus && orientationLandscape) {
hideSystemUI();
}
}

/**
* {@link View.OnSystemUiVisibilityChangeListener} 接口的回调操作,setOnSystemUiVisibilityChangeListener设置的哦
*
* @param visibility
*/
public void onSystemUiVisibilityChange(int visibility) {
// Note that system bars will only be "visible" if none of the
// LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set.

boolean orientationLandscape = isOrientationLandscape(getResources());

if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { //系统状态栏显示
if (orientationLandscape) {
mHandler.removeCallbacks(hideSystemUIAction);
mHandler.postDelayed(hideSystemUIAction, 100);

}

} else {//系统状态栏隐藏

}
}

/**
* 系统配置更新回调
* @param newConfig
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);

if (ActivityInfo.SCREEN_ORIENTATION_USER == newConfig.orientation) { //横屏
hideSystemUI();
mHandler.postDelayed(hideSystemUIAction, 500);
} else { //竖屏
mHandler.removeCallbacks(hideSystemUIAction);
showSystemUI();
}
}

/**
* 隐藏系统状态栏和导航栏
*/
private void hideSystemUI() {
//使用SYSTEM_UI_FLAG_IMMERSIVE_STICKY模式,
// 即:粘性沉浸模式,如果用户从隐藏了系统栏的边缘滑动,系统栏会显示出来,但它们是半透明的,
// 并且轻触手势会传递给应用,因此应用也会响应该手势
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
// Set the content to appear under the system bars so that the
// content doesn't resize when the system bars hide and show.
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
// Hide the nav bar and status bar
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN);
}

/**
* 显示系统状态栏和导航栏
*/
private void showSystemUI() {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_VISIBLE);
}

/**
* 判断当前界面是否为横屏
*
* @param resources
* @return
*/
protected boolean isOrientationLandscape(Resources resources) {
if (resources == null) {
return false;
}
return Configuration.ORIENTATION_LANDSCAPE == resources.getConfiguration().orientation;
}

改动点主要有以下几点:

  1. 通过 View.setOnSystemUiVisibilityChangeListener()注册 View.OnSystemUiVisibilityChangeListener ,以便获得系统界面可见性更改的通知

  2. 实现 onWindowFocusChanged()。 如果在横屏下获得窗口焦点,则需要再次隐藏系统栏

  3. 隐藏操作通过Handler进行适当延迟

  4. 使用粘性沉浸模式(SYSTEM_UI_FLAG_IMMERSIVE_STICKY),在普通的沉浸模式中,只要用户从边缘滑动,系统就会负责显示系统栏,您的应用甚至不会知道发生了该手势。因此,如果用户实际上可能是出于主要的应用体验而需要从屏幕边缘滑动,例如在玩需要大量滑动的游戏或使用绘图应用时,您应改为启用“粘性”沉浸模式。

    在粘性沉浸模式下,如果用户从隐藏了系统栏的边缘滑动,系统栏会显示出来,但它们是半透明的,并且轻触手势会传递给应用,因此应用也会响应该手势。

源码

源码仓库:https://gitee.com/goldsea/learn.git

核心源码:FullScreenActivityBaseAppCompatActivity

参考

启用全屏模式

相应界面可见性更改

坚持原创技术分享,您的支持是对我最大的鼓励!