沉浸式状态栏、导航栏机型适配

本文介绍了一种在Android应用中实现状态栏美化的方法,包括APP视图嵌入状态栏、动态控制导航栏显示隐藏以及动态设置状态栏文字颜色等功能。支持多种系统版本和机型。


        最近把状态栏和导航栏相关的东西整理了一下,目前应用到了项目里面,测试了4.4 5.x 6.x 7.x 8.0的系统,包括oppo vivo  魅族 华为 小米 酷派等机型,都是可以的,也花了点时间,所以在这里记录一下

完成后发现界面确实美观很多~~看来没白忙活啊  啊哈哈

特点:

    1.APP视图嵌入到状态栏

    2.底部虚拟导航栏显示隐藏是动态控制视图高度

    3.动态设置状态栏文字颜色(适配了魅族、小米及大部分6.0以上系统的手机)

注意事项:

    1.如需设置4.4-5.0系统的状态栏颜色则需引入第三方库(不需要设置删除对应报错代码即可)

com.readystatesoftware.systembartint:systembartint:1.0.3
    2.无需在布局中添加
android:fitsSystemWindows=""

   通过下面的方式设置状态栏或导航栏padding,避免视图重叠

view.setPadding(0, 0, 0, ScreenUtil.getNavigationBarHeight());//这里是设置底部导航栏padding,状态栏对应改成获取状态栏高度再设置即可

    3.最好不在style中定义类似的属性(我最开始就是通过定义style属性来实现,但发现不能满足需求,比如不能动态更改状态栏文字颜色等等,所以还是在代码里设置好)


代码:

  1. public class StatusBarActivity extends AppCompatActivity implements ViewTreeObserver.OnGlobalLayoutListener {
  2. private int result;
  3. private View contentView;
  4. //上次的可用高度
  5. private int usableHeightPrevious;
  6. private ViewGroup.LayoutParams frameLayoutParams;
  7. private ViewTreeObserver viewTreeObserver;
  8. @Override
  9. public void setContentView(int layoutResID) {
  10. super.setContentView(layoutResID);
  11. init();
  12. }
  13. @Override
  14. public void setContentView(View view) {
  15. super.setContentView(view);
  16. init();
  17. }
  18. @Override
  19. public void setContentView(View view, ViewGroup.LayoutParams params) {
  20. super.setContentView(view, params);
  21. init();
  22. }
  23. private void init() {
  24. //默认设置亮色statusBar,以适用白色主题
  25. setStatusBarModel( true);
  26. //在有虚拟导航栏的手机添加ViewTreeObserver,动态更改视图高度以适应虚拟键盘
  27. if (ScreenUtil.checkDeviceHasNavigationBar(getApplicationContext())) {
  28. contentView = findViewById(android.R.id.content);
  29. if (contentView != null) {
  30. viewTreeObserver = contentView.getViewTreeObserver();
  31. viewTreeObserver.addOnGlobalLayoutListener( this);
  32. frameLayoutParams = contentView.getLayoutParams();
  33. }
  34. }
  35. //如果手机有底部导航栏,则腾出导航栏同高度的padding,避免导航栏遮挡布局内容,这个方法不适用于动态隐藏、显示导航栏
  36. // if (ScreenUtil.checkDeviceHasNavigationBar(getApplicationContext())) {
  37. // getWindow().getDecorView().findViewById(android.R.id.content).setPadding(0, 0, 0, ScreenUtil.getNavigationBarHeight(getApplicationContext()));
  38. // } else {
  39. // getWindow().getDecorView().findViewById(android.R.id.content).setPadding(0, 0, 0, 0);
  40. // }
  41. }
  42. /**
  43. * 设置状态栏文字及图标颜色
  44. *
  45. * @param dark true状态栏文字及图标颜色设置为深色,false:白色
  46. * Created by ly on 2018/4/17 17:12
  47. */
  48. public void setStatusBarModel(boolean dark) {
  49. setTransBar();
  50. if (dark) {
  51. StatusBarLightMode();
  52. } else {
  53. StatusBarDarkMode();
  54. }
  55. }
  56. private void setTransBar() {
  57. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //5.0及以上
  58. View decorView = getWindow().getDecorView();
  59. int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
  60. | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
  61. decorView.setSystemUiVisibility(option);
  62. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  63. getWindow().setStatusBarColor(getColor(R.color.transparent));
  64. } else {
  65. getWindow().setStatusBarColor(getResources().getColor(R.color.half_transparent));
  66. }
  67. } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //4.4到5.0
  68. WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
  69. localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
  70.              //TODO 这里切换成你对应的全屏activity即可
  71. if (! "SplashActivity".equals(getClass().getSimpleName())
  72. && ! "GuideActivity".equals(getClass().getSimpleName())) { //启动页、引导页等需要全屏的页面不添加半透明遮罩
  73. SystemBarTintManager tintManager = new SystemBarTintManager( this);
  74. tintManager.setStatusBarTintEnabled( true);
  75. tintManager.setNavigationBarTintEnabled( false);
  76. tintManager.setStatusBarTintResource(R.color.status_half_transparent); //为状态栏添加半透明遮罩,避免里面的文字被覆盖
  77. }
  78. }
  79. }
  80. private void StatusBarLightMode() {
  81. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  82. if (MIUISetStatusBarMode( true)) {
  83. result = 1;
  84. } else if (FlymeSetStatusBarMode( true)) {
  85. result = 2;
  86. } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  87. //android6.0以后对状态栏文字颜色和图标改为暗色
  88. getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
  89. result = 3;
  90. }
  91. }
  92. }
  93. /**
  94. * 状态栏暗色模式,清除MIUI、flyme或6.0以上版本状态栏黑色文字、图标
  95. */
  96. private void StatusBarDarkMode() {
  97. if (result == 1) {
  98. MIUISetStatusBarMode( false);
  99. } else if (result == 2) {
  100. FlymeSetStatusBarMode( false);
  101. } else if (result == 3) {
  102. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  103. getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
  104. } else {
  105. getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
  106. }
  107. }
  108. }
  109. /**
  110. * 设置状态栏图标为深色和魅族特定的文字风格
  111. * 可以用来判断是否为Flyme用户
  112. * <p>
  113. * 详情:https://www.jianshu.com/p/7f5a9969be53
  114. *
  115. * @param window 需要设置的窗口
  116. * @param dark 是否把状态栏文字及图标颜色设置为深色
  117. * @return boolean 成功执行返回true
  118. */
  119. private boolean FlymeSetStatusBarMode(boolean dark) {
  120. boolean result = false;
  121. try {
  122. WindowManager.LayoutParams lp = getWindow().getAttributes();
  123. Field darkFlag = WindowManager.LayoutParams.class
  124. .getDeclaredField( "MEIZU_FLAG_DARK_STATUS_BAR_ICON");
  125. Field meizuFlags = WindowManager.LayoutParams.class
  126. .getDeclaredField( "meizuFlags");
  127. darkFlag.setAccessible( true);
  128. meizuFlags.setAccessible( true);
  129. int bit = darkFlag.getInt( null);
  130. int value = meizuFlags.getInt(lp);
  131. if (dark) {
  132. value |= bit;
  133. } else {
  134. value &= ~bit;
  135. }
  136. meizuFlags.setInt(lp, value);
  137. getWindow().setAttributes(lp);
  138. result = true;
  139. setStatusBarModel4M(dark);
  140. } catch (Exception e) {
  141. }
  142. return result;
  143. }
  144. /**
  145. * 需要MIUIV6以上
  146. * <p>
  147. * 详情:https://www.jianshu.com/p/7f5a9969be53
  148. *
  149. * @param activity
  150. * @param dark 是否把状态栏文字及图标颜色设置为深色
  151. * @return boolean 成功执行返回true
  152. */
  153. private boolean MIUISetStatusBarMode(boolean dark) {
  154. boolean result = false;
  155. try {
  156. Class clazz = getWindow().getClass();
  157. int darkModeFlag = 0;
  158. Class layoutParams = Class.forName( "android.view.MiuiWindowManager$LayoutParams");
  159. Field field = layoutParams.getField( "EXTRA_FLAG_STATUS_BAR_DARK_MODE");
  160. darkModeFlag = field.getInt(layoutParams);
  161. Method extraFlagField = clazz.getMethod( "setExtraFlags", int.class, int.class);
  162. if (dark) {
  163. extraFlagField.invoke(getWindow(), darkModeFlag, darkModeFlag); //状态栏透明且黑色字体
  164. } else {
  165. extraFlagField.invoke(getWindow(), 0, darkModeFlag); //清除黑色字体
  166. }
  167. result = true;
  168. setStatusBarModel4M(dark);
  169. } catch (Exception e) {
  170. }
  171. return result;
  172. }
  173. private void setStatusBarModel4M(boolean dark) {
  174. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  175. //开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错,所以两个方式都要加上
  176. if (dark) {
  177. getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
  178. } else {
  179. getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
  180. }
  181. }
  182. }
  183. @Override
  184. public void onGlobalLayout() {
  185. Rect r = new Rect();
  186. contentView.getWindowVisibleDisplayFrame(r);
  187. int usableHeightNow = r.bottom;
  188. //TODO 注意这个比例是华为mate10的比例,一般超出该值就代表不是导航栏显隐了(有可能是键盘等等..)
  189. boolean isNavBarChange = usableHeightNow / ( float) (ScreenUtil.getScreenHeight()) >= 1808 / 1920f;
  190. Logger.i( "onGlobalLayout usableHeightNow:" + usableHeightNow + " isNavBarChange:" + isNavBarChange);
  191. //当前可用高度和上次的不相等并且是导航栏展开/隐藏 则调整视图高度
  192. if (usableHeightNow != usableHeightPrevious && isNavBarChange) {
  193. frameLayoutParams.height = usableHeightNow;
  194. contentView.requestLayout();
  195. usableHeightPrevious = usableHeightNow;
  196. }
  197. }
  198. @Override
  199. protected void onDestroy() {
  200. super.onDestroy();
  201. if (viewTreeObserver != null) {
  202. if (viewTreeObserver.isAlive())
  203. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  204. viewTreeObserver.removeOnGlobalLayoutListener( this);
  205. } else {
  206. viewTreeObserver.removeGlobalOnLayoutListener( this);
  207. }
  208. viewTreeObserver = null;
  209. }
  210. contentView = null;
  211. frameLayoutParams = null;
  212. }
  213. }

用你的baseActivity继承这个就可以用啦

尽情玩耍吧~~~


如果有什么疑问或有更好的实现方式,欢迎留言给我


ps:参考了这位同学的实现,感谢~~去看看


版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.youkuaiyun.com/xiaoerye/article/details/79994973
个人分类: android

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值