【Android】透明状态栏在App中的实现与接口设计
2017-01-20 18:57
423 查看
Sodino 文章目录1. 认识透明状态栏
2. 透明状态栏Api及特性
3. 设置透明状态栏
4. 处理消失的系统状态栏区域
5. fitsSystemWindows
6. Activity中的接口设计
7. Fragment中的接口设计
8. 白色Titlebar的处理
9. React-Native的处理
10. 小米 与 魅族 与 (莫名其妙的)华为
11. 腾讯优测UTest
GitHub源码:TransparentStatusbar
源码中分两个
白色/红色
Activity中的接口设计
Fragment中的接口设计
见下图,左边为传统的
正常显示状态栏的图标/文字
状态栏的背景是透明的,能透出应用的背景色.而不像之前一样是默认的黑色不可编辑.
下表展示各版本所引入的新
注意: 要设置透明状态栏的
代码执行后,界面显示效果如下图:
可以发现系统状态栏的区域已经消失,
导致虽然
调整
对于不同的Android版本,可以通过版本适配文件,在
调整
不适应无
从
方式单一,对于指定的界面实现简单,但要应用于整个
不够灵活还表现在:有些
在
在实践中,本人采用了
这里只记录一下系统源码中的相应的方法:
调试系统源码的一个方法:
使用
如不要求强制使用
不影响正常的业务Activity的java代码编写
如业务Actiivty不需要额外的编码量即可实现透明状态栏效果.特殊的动效Activity除外.
提供灵活的处理方式
可方便的开启或关闭透明状态栏功能.
类图如下:
由于透明状态栏与
默认
该类中几个重要函数的调用顺序为:
具体代码实现为:
注意 : 由于透明状态栏是执行
阅读该
重载了
扩展此功能,即在添加通用
其中,为了便于定位到
那么在哪个时机处理
阅读
Java代码实现如下:
通过白色
但在是低版本的手机中,
这时可以通过
见如下代码或
第一个
第二个
第三个
最张效果见下图:
不不,
将
这点两家做得很好.这里直接给出官方文档说明了.小米状态栏变色
魅族状态栏变色上述的代码也整合进了GitHub中的工程
出了华为这档子事,就把App上传试了下其它各种手机,还好还好,没发现其它妖娥子.
About Sodino
2. 透明状态栏Api及特性
3. 设置透明状态栏
4. 处理消失的系统状态栏区域
5. fitsSystemWindows
6. Activity中的接口设计
7. Fragment中的接口设计
8. 白色Titlebar的处理
9. React-Native的处理
10. 小米 与 魅族 与 (莫名其妙的)华为
11. 腾讯优测UTest
GitHub源码:TransparentStatusbar
源码中分两个
app
TestBasic:透明状态栏实现的示例,方便debug
白色/红色
Titlebar的不同处理方式
paddingTop与
fitsSystemWindows的对比
layer-list分层背景的使用
TitlebarBelowTransparentStatusBar示例App中统一的处理方式
Activity中的接口设计
Fragment中的接口设计
认识透明状态栏
从Android4.4开始引入了透明状态栏的新特性.
见下图,左边为传统的
Android系统状态栏,右边为透明状态栏.
正常显示状态栏的图标/文字
状态栏的背景是透明的,能透出应用的背景色.而不像之前一样是默认的黑色不可编辑.
透明状态栏Api及特性
从Android 4.4(v19)开始,透明状态栏特性变化很频繁,直到
Android 6.0(v23)才真正完善稳定.
下表展示各版本所引入的新
Api或特性.
Version/level | Features | Description | |
---|---|---|---|
4.4/v19 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | 状态栏是渐变色的半透明 | |
4.4_Watch/v20 | OnApplyWindowInsetsListener | 能够区分多个Inset事件与Rect信息(PS.系统状态栏属于插入区Inset的一种) | |
5.0/v21 | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN View.SYSTEM_UI_FLAG_LAYOUT_STABLE | 允许自定义状态栏背景色了,但无法控制状态栏上的文字/图标颜色 | |
6.0/v23 | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | 状态栏上的图标/文字颜色的亮色模式,即颜色是暗色 |
设置透明状态栏
根据多个版本间的Api及特性,
Java代码如下:
12345678910111213141516171819 | // Activity.java// onCreate(Bundle bundle) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Android 5.0 int visibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // Android 6.0 // 亮色模式,避免系统状态栏的图标不可见 // visibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } window.getDecorView().setSystemUiVisibility(visibility); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); // 自定义状态栏背景色 window.setStatusBarColor(Color.TRANSPARENT); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // Android 4.4 window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } |
Activity其
theme须是
NoTitleBar.
12345678910 | // AndroidManifest.xml <activity android:name="MyActivity" android:theme="@android:style/Theme.NoTitleBar" /> // Or / 或 // MyActivity.java public void onCreate(Bundle bundle) { super.onCreate(bundle); requestWindowFeature(Window.FEATURE_NO_TITLE); } |
可以发现系统状态栏的区域已经消失,
Activity的contentView顶上去占据了原来属于系统状态栏的区域.
导致虽然
Back
Title虽然仍在
Titlebar区域垂直居中,但视觉效果上受状态栏图标的影响,却不是垂直居中的效果.所以接下来第二步就是: 以何种方式处理消失的系统状态栏区域?
处理消失的系统状态栏区域
处理方式可以有:就让activity的
contentView顶上去吧,不需要修改.这种场景用于一些可全屏浏览图片/观看视频等界面.
调整
Titlebar的高度.可以通过设置
paddingTop/
layout_height.
对于不同的Android版本,可以通过版本适配文件,在
values/dimens.xml和
values-v19/dimens.xml分别定义具体数值.
调整
paddingTop,有可能导致
Titlebar中的内容不会再垂直居中.
不适应无
Titlebar的
activity.
android:fitsSystemWindows&&
OnApplyWindowInsetsListener
fitsSystemWindows标签可以直接对
View添加
paddingXXX
从
activity的布局嵌套结构中只对第一个设置
fitsSystemWidnows的
View有效,无法设置设置到多个
View
方式单一,对于指定的界面实现简单,但要应用于整个
App超多
Activity的
layout.xml,不够灵活.
不够灵活还表现在:有些
activity的复杂效果可能会有多个
View同时或分场合占据系统状态栏的空间,需要留出额外的修改接口.
在
activity的
contentView的顶部再
addView直接填充原来状态栏的区域.
OnApplyWindowInsetsListener可以回调给开发者当前WindowInset的区域类型与区域宽高Rect信息和
fitsSystemWindows一样,多个
View设置该监听但也只有最外层的view会被调用执行.
在实践中,本人采用了
1
2
5这三种方式配合使用.
fitsSystemWindows
我并不想用该属性.这里只记录一下系统源码中的相应的方法:
123456 | View.java public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) private boolean fitSystemWindowsInt(Rect insets) // 这个方法是真正为View添加paddingXXX的地方 protected void internalSetPadding(int left, int top, int right, int bottom) |
使用
Android自带模拟器
Debug,断点跟进执行过程.要注意,模拟器的
apk level要和
compileSdkVersion及
buildToolsVersion相对应.
Activity中的接口设计
接口设计的原则:对正常的业务布局xml的编写没有强制要求如不要求强制使用
fitsSystemWindows
不影响正常的业务Activity的java代码编写
如业务Actiivty不需要额外的编码量即可实现透明状态栏效果.特殊的动效Activity除外.
提供灵活的处理方式
可方便的开启或关闭透明状态栏功能.
类图如下:
BaseActivity是
App中所有
Activity的父类.
由于透明状态栏与
Activity相关,所以对应的接口声明都放在
BaseActivity中.
默认
Activity的透明状态栏功能是开启的.
该类中几个重要函数的调用顺序为:
12 | `onCreate` → `setContentView` → `isFixTransparentStatusbar` └──true→ `fixTransparentStatusbar` |
12345678910111213141516171819202122232425262728293031323334353637383940414243 | // WhateverActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // BaseActivity.java @Override public void setContentView(View view) { rootView = view; super.setContentView(view); if (isFixTransparentStatusBar()) { Window window = getWindow(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { int visibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 亮色模式,避免系统状态栏的图标不可见 visibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } window.getDecorView().setSystemUiVisibility(visibility); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); fixTransparentStatusBar(view); // 最后fix一下状态栏背景白色与系统的文字图标白色的问题 fixTransparentStatusBarWhiteTextColor(view, viewStatusbarBackground); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // WindowManager.LayoutParams localLayoutParams = window.getAttributes(); // localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags); window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); fixTransparentStatusBar(view); // 最后fix一下状态栏背景白色与系统的文字图标白色的问题 fixTransparentStatusBarWhiteTextColor(view, viewStatusbarBackground); } else { setStatusbarBackgroundGone(); } } else { setStatusbarBackgroundGone(); } } |
Window.addFlags()实现的,该方法又调用了
Window.setFlags().
阅读该
Api文档,发现推荐先执行
setContentView后执行
Window.setFlags().
TitlebarActivity是通用的包含
Titlebar的
Activity.
重载了
setContentView(),实现自动添加
Titlebar这个通用组件,当然不需要
Titlebar时也可以使用
setContentViewNoTitlebar().
扩展此功能,即在添加通用
Titlebar前先添加上通用的
viewStatusbarBackground.
setContentView()的实现为:
12345678910111213141516171819 | @Overridepublic void setContentView(View view) { contentView = view; LinearLayout linearLayout = new LinearLayout(this); linearLayout.setOrientation(LinearLayout.VERTICAL); LayoutInflater.from(this).inflate(R.layout.transparent_status_bar_bg_view, linearLayout, true); viewStatusbarBackground = linearLayout.findViewById(R.id.status_bar_background); LayoutInflater.from(this).inflate(R.layout.titlebar_original, linearLayout, true); viewTitlebar = linearLayout.findViewById(R.id.titlebar_layout); initTitlebarIDs(viewTitlebar); linearLayout.addView(contentView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); super.setContentView(linearLayout); } |
Titlebar及
viewStatusbarBackground,这两个组件的
id都被预先定义在
attrs.xml中.
12345 | // values/attrs.xml<resources> <item name="status_bar_background" type="id"/> <item name="titlebar_layout" type="id"/></resources> |
Fragment中的接口设计
有的Activity的显示主体是
Fragment,接口设计的观点为不应干扰
Fragment正常的
onCreateView()的实现流程.
那么在哪个时机处理
Fragment的
contentView呢?
阅读
Api发现了
Fragment::onViewCreated(View view)这个方法,该方法会在
onCreateView()返回后,立即执行,且方法参数为
onCreateView()所返回的
View.
Java代码实现如下:
12345678910111213141516171819202122232425262728 | public class BaseFragment extends Fragment { @Override public void onViewCreated(View view, Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && isFixTransparentStatusBar()) { fixTransparentStatusBar(view); } super.onViewCreated(view, savedInstanceState); } /** * 是否需要改变status bar背景色,对于某些机型手机(如oppo)无法改变状态栏字体颜色, * 会被当前状态栏挡住字体颜色,因此修改透明状态栏背景色 * @return true: 调用fixTransparentStatusBar() */ protected boolean isFixTransparentStatusBar(){ return false; } /** * @param view {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}中返回的view. * */ protected void fixTransparentStatusBar(View view) { }} |
fixTransparentStatusBar(),即可以调整
Fragment的界面显示,无论是往状态栏区域添加一个填充
View或根据
id再调整宽高或
padding都是可以的.
白色Titlebar
的处理
Android 6.0及以上可以使用亮色模式.
但在是低版本的手机中,
Titlebar如果是白色的,或者说
App的主题是白色的,则会出现状态栏的白色文字和图标被淹没在
Titlebar中无法阅读.如下图:
这时可以通过
layer-list来设置分层背景,不必新增额外的
View填充系统状态栏区域.
见如下代码或
TestBasic/res/drawable/title_layout_white3.xml:
123456789101112131415161718192021222324252627 | <?xml version="1.0" encoding="utf-8"?><layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:shape="rectangle"> <solid android:color="@android:color/black" /> </shape> </item> <item android:bottom="1dp"> <shape android:shape="rectangle"> <solid android:color="@android:color/white" /> </shape> </item> <!-- 48dp为标题栏高度 --> <item android:bottom="48dp"> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <gradient android:startColor="@android:color/white" android:centerColor="@color/middleColor" android:endColor="@android:color/darker_gray" android:angle="90" /> </shape> </item></layer-list> |
item为黑色背景,效果为
Titlebar底下的黑色分隔线.
第二个
item为常规的
Titlebar背景.
第三个
item为状态栏的过滤渐变背景色.
最张效果见下图:
React-Native的处理
React-Native是
js代码,怎么办?
不不,
React是表象,
Native是实质。一样处理掉。
React-Native最
root的组件界面是
ReactRootView,可以在显示的
Activity里布局使用
LinearLayout,
orientation为
VERTICAL,
将
TransparentStatusBar及
ReactRootView一并添加为子
View,设置该
LinearLayout为
Activity的
contentView即可。代码如下:
12345678910111213141516171819202122232425 | public class ElnReactBaseActivity extends BaseActivity { private ReactInstanceManager mReactInstanceManager; private ReactRootView mReactRootView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout linearLayout = new LinearLayout(this); linearLayout.setOrientation(LinearLayout.VERTICAL); viewStatusbarBackground = LayoutInflater.from(this).inflate(R.layout.transparent_status_bar_bg_view, linearLayout, false); linearLayout.addView(viewStatusbarBackground); mReactRootView = new ReactRootView(this); LinearLayout.LayoutParams layParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); linearLayout.addView(mReactRootView, layParams); mReactInstanceManager = ReactHelper.getInstance().getReactManager(); Bundle bundle = getExtra(); mReactRootView.startReactApplication(mReactInstanceManager, "ELearning", bundle); setContentView(linearLayout); }} |
小米 与 魅族 与 (莫名其妙的)华为
小米 与 魅族都能通过自各的反射方法实现状态栏的亮色模式,解决白色Titlebar的问题.
这点两家做得很好.这里直接给出官方文档说明了.小米状态栏变色
魅族状态栏变色上述的代码也整合进了GitHub中的工程
TitlebarBelowTransparentStatusBar.至于华为,额…大部分华为机子都是好机,但华为荣耀6 Plus(PE-TL10,EMUI3.1,Android 5.1.1)明明是Android 5.1,但使用5.1的代码无效,得使用4.4的实现方式.
腾讯优测UTest
一个方便使用的App远程测试平台,机型多,Android版本齐全.出了华为这档子事,就把App上传试了下其它各种手机,还好还好,没发现其它妖娥子.
About Sodino
相关文章推荐
- Android中设计模式--策略模式(封装会变化的算法部分,面向接口不针对实现)
- Android:透明状态栏的效果实现
- Android透明状态栏的实现
- 基于Android的旅游自助系统APP设计与实现
- Android 沉浸式状态栏的实现方法、状态栏透明
- Android 4.4 上实现透明导航栏和状态栏 Translucent system bar
- 一个android文本比对APP的实现(三)-设计模式在文件选择模块中的运用
- App开放接口api安全性—Token签名sign的设计与实现
- Android内容覆盖透明状态栏下实现全屏模式下带状态栏的效果
- Android即时聊天系统-随聊App之接口实现
- Android透明状态栏的实现(转)
- android 实现透明状态栏
- Android实现透明状态栏
- App开放接口api安全性—Token签名sign的设计与实现
- Android实现4.4以上系统状态栏透明
- Android 状态栏背景模糊透明效果实现
- Java课程设计笔记-Android 4.4系统实现沉浸式状态栏的正确姿势
- Android 4.4 上实现透明导航栏和状态栏 Translucent system bar
- Android 4.4 以上实现透明导航栏和状态栏 Translucent system bar
- App开放接口api安全性—Token签名sign的设计与实现