对比两种效果,很明显下面设置了沉浸式状态栏的看上去更协调、更美观一点。
2-如何实现沉浸式状态栏
2.1-Android 4.4以上实现方式
由于沉浸式状态栏设置是在Android 4.4之后才提供的,所以我们需要对Android 4.4以上的系统做适配。Android 4.4有两种方式可以实现沉浸式状态栏,一种是在资源文件中设置,一种是在代码中设置。
2.1.1-资源文件中设置沉浸式状态栏
首先,我们要修改values/styles.xml,在里面添加一个空的style,继承自BaseTheme。
然后将App的主题设置为AppTheme即可。 注:android:windowTranslucentStatus这个属性是v19开始引入的。
2.1.2-在代码中设置
在代码中实现更为方便一点,我们只需要在BaseActivity中添加一个FLAG_TRANSLUCENT_STATUS的flag即可。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
通过上述两种方法设置之后,效果图如下:
我们会发现,仅仅通过上述设置Toolbar会顶到状态栏里面去。通常大家会想到使用fitsSystemWindows属性来解决此问题。
fitSystemWindows官方描述:Boolean internal attribute to adjust view layout based on system windows such as the status bar. If true, adjusts the padding of this view to leave space for the system windows. Will only take effect if this view is in a non-embedded activity. 简单描述:这个属性的作用是让view可以根据系统窗口(如status bar)来调整自己的布局,如果值为true,就会调整view的paingding属性来给system windows留出空间(即给view添加一个值为状态栏高度的top padding)。
我们试着给Toolbar设置一下fitsSystemWindows属性为true。布局代码如下:
<?xml version="1.0" encoding="utf-8"?><android.support.v7.widget.Toolbar
android:id=“@+id/my_toolbar”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“@color/colorPrimary”
android:minHeight=“?attr/actionBarSize”
android:fitsSystemWindows=“true”
android:theme=“@style/ThemeOverlay.AppCompat.Dark.ActionBar” />
上面代码在Android 4.4和Android 5.0+上面对比效果图如下:
由上面对比图我们可以看出来,在Android 4.4上面状态栏是全透明的,而在Android 5.0+上面状态栏是半透明的。
注:有些4.4的系统上面状态栏并不是全透明的,而是渐变的。
2.2-Android 5.0以上实现方式
上面已经实现了沉浸式状态栏的效果了,但是如果运行在Android 5.0以上的机器上面,会发现大部分手机会出现状态栏是半透明的。
也有些App在Android 5.0以上就是这种状态栏半透明的效果,比如QQ。但是有些产品和设计就是想统一风格,全部都实现全透明的状态栏。那怎么办呢?Android自5.0起,又为我们提供了设置状态栏颜色的API,我们可以自己设置状态栏的颜色。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
}
添加上述代码后再在Android 5.0+上运行看效果,状态栏已经变成全透明了,和上图Android 4.4效果一样的,这里就不再附图了。
2.3-Android 6.0以上设置状态栏字体颜色
大部分手机默认状态栏字体颜色是白色的,如果Toolbar或者界面头部的颜色较浅,那么状态栏上白色的字看不怎么清楚。 Android 6.0以后,我们可以使用代码将状态栏字体的颜色设置为黑色了,代码如下:
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
3-踩过的坑
本以为上面基本已经完美实现了沉浸式状态栏了,没想到测试的时候还是发现了一系列的坑。
3.1-软键盘弹出时Toolbar被顶上去了
如果在界面中有EditText或者其他输入框的话,会发现当软件盘弹出的时候Toolbar里面的内容都被顶上去了,如下图所示:
这是为什么呢?经研究发现原来是fitsSystemWindows属性搞的鬼。哪个View设置了fitsSystemWindows=true,这个View就会被软件盘顶上去。所以说,fitsSystemWindows不能乱用,会有意想不到的坑。 那能不能不用fitsSystemWindows呢?当然可以。前面也说了,fitsSystemWindows=true的作用是给View增加值为状态栏高度的padding,那我们何不自己手动给Toolbar添加padding呢? 我们去掉Toolbar上的fitsSystemWindows属性,并设置一下Toolbar的padding,代码如下:
protected void setStatusBarPaddingAndHeight(View toolBar) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (toolBar != null) {
int statusBarHeight = getSystemBarHeight(this);
toolBar.setPadding(toolBar.getPaddingLeft(), statusBarHeight, toolBar.getPaddingRight(),
toolBar.getPaddingBottom());
toolBar.getLayoutParams().height = statusBarHeight +
(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 45, getResources().getDisplayMetrics());
}
}
}
去掉Toolbar的fitsSystemWindows属性,并加上上面的代码,软键盘弹出时Toolbar正常了。 目前随手记Android项目中就是使用代码添加padding的方式替代fitsSystemWindows属性的。
3.2-软键盘弹出时EditText等输入框会被软件盘覆盖掉
上面软件盘将Toolbar顶上去的示例图中,我们还会发现一个问题,就是软键盘弹出时EditText并没有跟着弹出来而是被软键盘覆盖掉了。
上面说Toolbar加了fitsSystemWindows属性之后会被软键盘顶上去,那么我们给输入框加一个fitsSystemWindows属性是否刚好就能解决输入框被覆盖的问题呢?果断试一下!
试了之后发现,果然可以,但是输入框的高度变了,其实是输入框的padding增加了状态栏的高度。很显然,这并不是一个很好的解决方式。 后来在stackoverflow上找到了一个解决方法:解决FLAG_TRANSLUCENT_STATUS导致输入框被软键盘覆盖的解决方案
我们对其做了点调整,代码如下:
public class AndroidBug5497Workaround {
public static void assistActivity(View content) {
new AndroidBug5497Workaround(content);
}
private View mChildOfContent;
private int usableHeightPrevious;
private ViewGroup.LayoutParams frameLayoutParams;
private AndroidBug5497Workaround(View content) {
if (content != null) {
mChildOfContent = content;
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
frameLayoutParams = mChildOfContent.getLayoutParams();
}
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
//如果两次高度不一致
//将计算的可视高度设置成视图的高度
frameLayoutParams.height = usableHeightNow;
mChildOfContent.requestLayout();//请求重新布局
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
//计算视图可视高度
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame®;
return r.bottom;
}
}
添加上面的类,然后在Activity的onCreate方法中的setContentView后面加上如下代码:
AndroidBug5497Workaround.assistActivity(findViewById(android.R.id.content));
然后运行,输入框能够正常被顶上去,而且输入框的布局又没有受到影响。
该方案的原理是,给界面的根布局设置一个监听器,当界面大小有变化的时候,如键盘弹出的时候,重新设置一下根布局的高度,再调用requestLayout对界面进行重绘。
目前随手记Android就是使用这个方案,截止到目前也没有发现这种方案会带来其他什么问题。
3.3-华为EMUI3.1上的坑
将上面的沉浸式代码放在EMUI3.1系统的手机(如华为荣耀7)上面跑,会发现根本没有沉浸式效果,状态栏是透明的,显示的是桌面上的颜色,如下图:
最后
针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
20235781)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!