【Android】透明状态栏在App中的实现与接口设计


By Sodino

文章目录

  1. 1. 认识透明状态栏
  2. 2. 透明状态栏Api及特性
  3. 3. 设置透明状态栏
  4. 4. 处理消失的系统状态栏区域
  5. 5. fitsSystemWindows
  6. 6. Activity中的接口设计
  7. 7. Fragment中的接口设计
  8. 8. 白色Titlebar的处理
  9. 9. 小米 与 魅族 与 (莫名其妙的)华为
  10. 10. 腾讯优测UTest

GitHub源码:TransparentStatusbar
源码中分两个app

  • TestBasic:

    1. 透明状态栏实现的示例,方便debug
    2. 白色/红色Titlebar的不同处理方式
    3. paddingTopfitsSystemWindows的对比
    4. layer-list分层背景的使用
  • TitlebarBelowTransparentStatusBar
    1. 示例App中统一的处理方式
    2. Activity中的接口设计
    3. 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代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19
// 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);   

}

注意: 要设置透明状态栏的Activitytheme须是NoTitleBar.

1

2

3

4

5

6

7

8

9

10
// 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区域垂直居中,但视觉效果上受状态栏图标的影响,却不是垂直居中的效果.

所以接下来第二步就是: 以何种方式处理消失的系统状态栏区域?


处理消失的系统状态栏区域

处理方式可以有:

  1. 就让activitycontentView顶上去吧,不需要修改.

    • 这种场景用于一些可全屏浏览图片/观看视频等界面.
  2. 调整Titlebar的高度.
    • 可以通过设置paddingTop/layout_height.
    • 对于不同的Android版本,可以通过版本适配文件,在values/dimens.xmlvalues-v19/dimens.xml分别定义具体数值.
    • 调整paddingTop,有可能导致Titlebar中的内容不会再垂直居中.
    • 不适应无Titlebaractivity.
  3. android:fitsSystemWindows && OnApplyWindowInsetsListener
    • fitsSystemWindows标签可以直接对View添加paddingXXX
    • activity的布局嵌套结构中只对第一个设置fitsSystemWidnowsView有效,无法设置设置到多个View
    • 方式单一,对于指定的界面实现简单,但要应用于整个App超多Activitylayout.xml,不够灵活.
    • 不够灵活还表现在:有些activity的复杂效果可能会有多个View同时或分场合占据系统状态栏的空间,需要留出额外的修改接口.
  4. activitycontentView的顶部再addView直接填充原来状态栏的区域.
  5. OnApplyWindowInsetsListener可以回调给开发者当前WindowInset的区域类型与区域宽高Rect信息
    • fitsSystemWindows一样,多个View设置该监听但也只有最外层的view会被调用执行.

在实践中,本人采用了1 2 5这三种方式配合使用.


fitsSystemWindows

我并不想用该属性.
这里只记录一下系统源码中的相应的方法:

1

2

3

4

5

6
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要和compileSdkVersionbuildToolsVersion相对应.


Activity中的接口设计

接口设计的原则:

  1. 对正常的业务布局xml的编写没有强制要求
    如不要求强制使用fitsSystemWindows
  2. 不影响正常的业务Activity的java代码编写
    如业务Actiivty不需要额外的编码量即可实现透明状态栏效果.特殊的动效Activity除外.
  3. 提供灵活的处理方式
    可方便的开启或关闭透明状态栏功能.

类图如下:

  • BaseActivity 是App中所有Activity的父类.
    由于透明状态栏与Activity相关,所以对应的接口声明都放在BaseActivity中.
    默认Activity的透明状态栏功能是开启的.
    该类中几个重要函数的调用顺序为:
1

2
`onCreate` → `setContentView` → `isFixTransparentStatusbar`   

└──true→ `fixTransparentStatusbar`

具体代码实现为:

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
// 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 是通用的包含TitlebarActivity.
    重载了setContentView(),实现自动添加Titlebar这个通用组件,当然不需要Titlebar时也可以使用setContentViewNoTitlebar().
    扩展此功能,即在添加通用Titlebar前先添加上通用的viewStatusbarBackground.
    setContentView()的实现为:
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19
@Override

public 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);  

}

其中,为了便于定位到TitlebarviewStatusbarBackground,这两个组件的id都被预先定义在attrs.xml中.

1

2

3

4

5
// values/attrs.xml

<resources>

<item name="status_bar_background" type="id"/>

<item name="titlebar_layout" type="id"/>

</resources>

Fragment中的接口设计

有的Activity的显示主体是Fragment,接口设计的观点为不应干扰Fragment正常的onCreateView()的实现流程.
那么在哪个时机处理FragmentcontentView呢?
阅读Api发现了Fragment::onViewCreated(View view)这个方法,该方法会在onCreateView()返回后,立即执行,且方法参数为onCreateView()所返回的View.

Java代码实现如下:

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
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:

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
<?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为状态栏的过滤渐变背景色.

最张效果见下图:


小米 与 魅族 与 (莫名其妙的)华为

小米 与 魅族都能通过自各的反射方法实现状态栏的亮色模式,解决白色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

时间: 2024-10-17 10:21:18

【Android】透明状态栏在App中的实现与接口设计的相关文章

Android透明状态栏

Android透明状态栏只有在4.4之后有. 其中设置有两种方式: 1. if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //透明状态栏 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //透明导航栏 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_

数据访问工具 DAF 中 ResourceIDService 服务的接口 设计与实现

http://www.paper.edu.cn- 1 -数据访问工具 DAF 中 ResourceIDService 服务的接口设计与实现服务的接口设计与实现陶昕,高锦标华东交通大学信息工程学院,南昌(330013)E-mail:[email protected]摘 要:ResourceID 是 64 位整型数据,是用来定位能量系统中唯一性资源的标识,ResourceIDService 服务是 Data Access Facility 规范简称为 DAF 中的一个重要服务接口,提供了 Resou

Android 透明状态栏&amp;着色状态栏

Android 5.0 及以上实现方式(android在5.0之后引入Material Design 实现方式相对简单) 透明状态栏,背景浸入状态栏 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } 在布局文件中View 默认f

android 透明状态栏方法及其适配键盘上推(二)

在上一篇文章中介绍了一种设置透明状态栏及其适配键盘上推得方法.但是上一篇介绍的方法中有个缺点,就是不能消除掉statusbar的阴影.很多手机如(三星,Nexus都带有阴影).即使我用了: <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" app:elevation="

Android透明状态栏。适用于 4.4 以上及 5.0以上设备

概述 有时候我们想在 andorid 手机上实现一种 跨越 顶部状态栏的效果,比如一张图片直接显示在 状态栏内.比如下图: 这个页面里有张图片,这个图片显示在整个页面的上部分.状态栏是 漂浮在这个图片上的. 实现透明状态栏的方法 适配Android 4.4 +的方法: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Window window = activity.getWindow(); // Translucent sta

android 透明状态栏方法及其适配键盘上推(一)

android的状态栏(statusBar)版本的差异化比较大.在android 4.4 以上和5.x可以设置状态栏背景颜色,但是不可以设置状态栏中字和图标的颜色.而系统默认的statusbar的字体和图标颜色为白色.如果在6.0以下的要实现透明状态栏(也就是把整个界面延伸到statusbar),就要考虑到如果您的应用背景颜色为白色的时候,会出现statusbar里的内容都看不清楚,这一点暂时是没办法去适配的.但是6.0以上的是既能修改statusbar的背景颜色,也可以修改statusbar的

Android 透明状态栏

if(VERSION.SDK_INT >= VERSION_CODES.KITKAT) {    //4.4以上                                 //透明状态栏                                 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);                                 //透明导航栏        

APP服务端API(数据接口)设计应该考虑到的问题

1.跨平台性 2.良好的响应速度 3.接口要为移动客户端考虑 4.考虑移动端的网络情况和耗电量 5.通用的数据交换格式 6.接口统计功能 7.客户端与服务端的肥瘦平衡 8.隐式用户与显式用户 9.安全问题 10.良好的接口说明文档和测试程序 11.版本的维护 详细分析请参考 :https://www.hutuseng.com/article/how-to-design-api 原文地址:http://blog.51cto.com/825272560/2058638

Android安全开发之WebView中的地雷

0X01 About WebView 在Android开发中,经常会使用WebView来实现WEB页面的展示,在Activiry中启动自己的浏览器,或者简单的展示一些在线内容等.WebView功能强大,应用广泛,但它是天使与恶魔的合体,一方面它增强了APP的上网体验,让APP功能更多样化,另一方面它也引入了很多的安全问题.在过去几年WebView中被披露的重大漏洞包括了任意代码执行漏洞.跨域.密码明文保存等,这些安全问题可以直接导致用户敏感信息泄露,移动终端被恶意攻击者控制.下文将详细介绍这一系