热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

适配安卓沉浸式状态栏的新姿势

GithubDemo:https:github.comlliuguangboAutoSystemBar针对状态栏,官方从4.4版本开始支持,但是4.4和5.0以上API是不同的,6

Github Demo: https://github.com/lliuguangbo/AutoSystemBar

针对状态栏,官方从4.4版本开始支持,但是4.4和5.0以上API是不同的,6.0以上提供了两种状态栏图标样式
分别是白色和黑色样式。

针对状态栏图标样式的修改,小米和魅族提供额外的API,在6.0以下都支持,可以参考它们的文档:

  • https://dev.mi.com/console/doc/detail?pId=1159
  • http://open-wiki.flyme.cn/index.php?title=%E7%8A%B6%E6%80%81%E6%A0%8F%E5%8F%98%E8%89%B2
Android4.4状态栏的API:

window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

设置该属性后,Activity的Layout会嵌入到状态栏下,
也就是setContentView(View)的set进去的view高度比之前高出了状态栏的高度。

Android5.0以上状态栏的API:

View decorView = window.getDecorView();
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);

5.0 以上 FLAG_TRANSLUCENT_STATUS 与 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS是有冲突的,需要调用clearFlags清楚这个flag否则会没有效果。 通过调用setStatusBarColor()来改变状态栏颜色。
但是当设置的颜色不是全透明时,Activity的setContentView(View)的set进去的view高度是没有发生变化的,如果设置的颜色是去透明时,setContentView(View)的set进去的view高度同样比之前高出了状态栏的高度。

官方Android6.0修改状态栏图标样式API

View decorView = window.getDecorView();
if(darkFont){
//黑色样式
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}else {
//白色样式
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}

适配状态栏的方案思考
  • 现在越来越多的应用开始适配状态栏了,不再是黑黑的状态栏;
  • 越来越多的应用会使用透明状态栏,把图片或者视屏嵌入到状态栏下充分利用空间,通常这个界面是可以滚动的,当滚动一定的位置时改变状态栏颜色。
  • 状态栏图标样式的适配

开发者通常的适配方法是:

  • 修改状态栏颜色,4.4版本需要额外添加一个状态栏高度的View来填充,通过改变这个View的背景来实现;5.0以上有两种方法,一种是调用setStatusBarColor(), 另一种和4.4的方案一致。
  • 这种情况适配起来还是挺麻烦的,透明状态栏时,activity的contentView的高度高出了状态栏的高度会引起一些布局上的问题,例如图片嵌入状态栏下,当滚动时ActionBar会重新出现,会导致ActionBar也嵌入状态栏下,很难看。适配时还要对不同的版本进行布局的调整(因为4.4以下没问题)
  • 对于状态栏图标样式的适配,如果你的ActionBar背景是白色的,状态栏改为白色,那么状态栏图标样式就需要改成黑色,如果还是白色会导致看不清状态栏图标

总的来说,对于第二种情况时适配还是挺麻烦的。

  • 采用额外添加一个状态栏高度的View来填充的方法可以统一4.4和5.0版本;
  • 获取ActionBar下最多的颜色,使用该颜色当做状态栏颜色,这时想到了官方提供com.android.support:palette-v7:26.1.0
  • 状态栏图标的样式根据状态栏颜色做出调整。
代码的实现

先撸个自定义ViewGroup,继承RelativeLayout。
下面是这个自定义ViewGroup需要加载的布局文件,分三部分:状态栏View,底部导航栏View, 中间内容部分


android:layout_
android:layout_>

android:id="@+id/status_view"
android:layout_
android:layout_
android:visibility="gone"
android:layout_alignParentTop="true"
/>
android:id="@+id/navigation_view"
android:layout_
android:layout_
android:visibility="gone"
android:layout_alignParentBottom="true"
/>

下面是自定义ViewGroup关键代码,命名为InternalLayout,在构造器里对状态栏View,底部导航栏View初始化高度。

class InternalLayout extends RelativeLayout{
// 部分关键代码
public InternalLayout(Context context, AttributeSet attrs) {
super(context, attrs);
Utils.initializeStatusBar(this);
Utils.initializeNavigationBar(this);
ViewCompat.setFitsSystemWindows(this, true);
ViewCompat.requestApplyInsets(this);
ViewCompat.setOnApplyWindowInsetsListener(this, new android.support.v4.view.OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
return insets.consumeSystemWindowInsets();
}
});
inflate(context, R.layout.layout_content, this);
mStatusView = findViewById(R.id.status_view);
mStatusView.getLayoutParams().height = Utils.sStatusBarHeight;
mNavigatiOnView= findViewById(R.id.navigation_view);
mNavigationView.getLayoutParams().height = Utils.sNavigationBarHeight;
mCOntentLayout= (ViewGroup) findViewById(R.id.content);
}
void setContentView(View content) {
if(content.getParent() == null){
mContentLayout.addView(content);
}
}
}

下面是SystemBarView的代码, android4.4以下SystemBarView高度,宽带都为0。

class SystemBarView extends View{
public SystemBarView(Context context) {
super(context);
}
public SystemBarView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}else {
setMeasuredDimension(0, 0);
setVisibility(GONE);
}
}
}

InternalLayout内部有一个FrameLayout,用来装Activity的setContentView()里View,代码如下:

final View decorView = window.getDecorView();
final View androidCOntent= window.getDecorView().findViewById(Window.ID_ANDROID_CONTENT);
final ViewGroup realCOntent= ((ViewGroup) androidContent);
View cOntent= realContent.getChildAt(0);
//content是Activity的setContentView()里的View
realContent.removeView(content);
InternalLayout layout = new InternalLayout(activity);
layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
layout.setContentView(content);
realContent.addView(layout);

接着实现在ActionBar里获取主颜色作为状态栏的颜色:

//decorView是Activity的DecorView, 获取decorView的bitmap
decorView.setDrawingCacheEnabled(true);
final Bitmap bitmap = Bitmap.createBitmap(decorView.getDrawingCache());
decorView.setDrawingCacheEnabled(false);
int top = Utils.sStatusBarHeight + PADDING;
int bottom = (int) (top + ACTION_BAR_DEFAULT_HEIGHT * decorView.getResources().getDisplayMetrics().density);
//PADDING = 10, ACTION_BAR_DEFAULT_HEIGHT = 48, rect 记录了一个矩形,在状态栏下面的一个矩形,10, 48 我随便定义的距离,这个矩形不用太精准,不用就是ActionBar的位置。
Rect rect = new Rect(0, top, bitmap.getWidth(), bottom);
//利用Palette的API来获取颜色
mBuilder = new Palette.Builder(bitmap)
.clearFilters()
.addFilter(FILTER)
.setRegion(rect.left, rect.top, rect.right, rect.bottom);
Palette p = mBuilder.generate()
//这里会获取一个List, Swatch就是代表获取到一种颜色,
// getPopulation() :返回这颜色的数量。
// getRgb(): 返回rgb颜色
List swatches = new ArrayList<>(palette.getSwatches());
//给swatches排排序,获取数量最大的那个
Collections.sort(swatches, new Comparator() {
@Override
public int compare(Palette.Swatch lhs, Palette.Swatch rhs) {
if (lhs == null && rhs != null) {
return 1;
} else if (lhs != null && rhs == null) {
return -1;
} else if (lhs == null) {
return 0;
} else {
return rhs.getPopulation() - lhs.getPopulation();
}
}
});
populatiOnSwatch= swatches.get(0);
color = populationSwatch.getRgb();//color就是我需要的颜色,把它设置到状态栏即可

根据颜色去判断使用状态栏图标样式:


private static final float BLACK_MAX_LIGHTNESS = 0.05f;
private static final float WHITE_MIN_LIGHTNESS = 0.95f;
// 将color转成hsl色彩模式
private boolean isDarkStyle(int color) {
boolean isDarkStyle = false;
//mTemp是一个float数组,数组长度为3,数组的值依次为对色相(H)、饱和度(S)、明度(L)
ColorUtils.colorToHSL(color, mTemp);
if (mTemp[2] <= BLACK_MAX_LIGHTNESS) {
isDarkStyle = false; // 白色
} else if (mTemp[2] >= WHITE_MIN_LIGHTNESS) {
isDarkStyle = true; // 黑色
}
return isDarkStyle;
}

最后我将实践写成了一个库: https://github.com/lliuguangbo/AutoSystemBar

使用起来很简单, 有问题可以提issue, 也可以star以下表示支持,谢谢.

//1.
SystemBarHelper.Builder().into(activity)
//2.
SystemBarHelper.Builder()
.statusBarColor() // 设置状态栏颜色
.statusBarFontStyle() // 设置状态栏时间,电量的风格, 6.0以上, 部分国产机可以不用6.0以上.
.navigationBarColor() // 设置导航栏颜色
.enableImmersedStatusBar() // 布局嵌入状态栏,例如图片嵌入状态栏
.enableImmersedNavigationBar() // 布局嵌入导航栏,例如图片嵌入导航栏
.enableAutoSystemBar(false) // 根据状态栏下面的背景颜色自动调整状态栏的颜色, 自动调整状态栏时间,电量的风格, 默认是开启的
.into(this)

//3.
SystemBarHelper helper = SystemBarHelper.Builder().into(activity);
helper.setNavigationBarColor()
helper.setStatusBarColor()
helper.statusBarFontStyle()
helper.enableImmersedStatusBar()
helper.enableImmersedNavigationBar()

推荐阅读
  • 技术分享:深入解析GestureDetector手势识别机制
    技术分享:深入解析GestureDetector手势识别机制 ... [详细]
  • 本文探讨了资源访问的学习路径与方法,旨在帮助学习者更高效地获取和利用各类资源。通过分析不同资源的特点和应用场景,提出了多种实用的学习策略和技术手段,为学习者提供了系统的指导和建议。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 利用树莓派畅享落网电台音乐体验
    最近重新拾起了闲置已久的树莓派,这台小巧的开发板已经沉寂了半年多。上个月闲暇时间较多,我决定将其重新启用。恰逢落网电台进行了改版,回忆起之前在树莓派论坛上看到有人用它来播放豆瓣音乐,便萌生了同样的想法。通过一番调试,终于实现了在树莓派上流畅播放落网电台音乐的功能,带来了全新的音乐享受体验。 ... [详细]
  • 掌握Android UI设计:利用ZoomControls实现图片缩放功能
    本文介绍了如何在Android应用中通过使用ZoomControls组件来实现图片的缩放功能。ZoomControls提供了一种简单且直观的方式,让用户可以通过点击放大和缩小按钮来调整图片的显示大小。文章详细讲解了ZoomControls的基本用法、布局设置以及与ImageView的结合使用方法,适合初学者快速掌握Android UI设计中的这一重要功能。 ... [详细]
  • ### 摘要`mkdir` 命令用于在指定位置创建新的目录。其基本格式为 `mkdir [选项] 目录名称`。通过该命令,用户可以在文件系统中创建一个或多个以指定名称命名的文件夹。执行此操作的用户需要具备相应的权限。此外,`mkdir` 还支持多种选项,如 `-p` 用于递归创建多级目录,确保路径中的所有层级都存在。掌握这些基本用法和选项,有助于提高在 Linux 系统中的文件管理效率。 ... [详细]
  • 在Android平台上,视频监控系统的优化与应用具有重要意义。尽管已有相关示例(如http:www.open-open.comlibviewopen1346400423609.html)展示了基本的监控功能实现,但若要提升系统的稳定性和性能,仍需进行深入研究和优化。本文探讨了如何通过改进算法、优化网络传输和增强用户界面来提高Android视频监控系统的整体效能,以满足更复杂的应用需求。 ... [详细]
  • 初探性能优化:入门指南与实践技巧
    在编程领域,常有“尚未精通编码便急于优化”的声音。为了从性能优化的角度提升代码质量,本文将带领读者初步探索性能优化的基本概念与实践技巧。即使程序看似运行良好,数据处理效率仍有待提高,通过系统学习性能优化,能够帮助开发者编写更加高效、稳定的代码。文章不仅介绍了性能优化的基础知识,还提供了实用的调优方法和工具,帮助读者在实际项目中应用这些技术。 ... [详细]
  • 在 CentOS 6.5 系统上部署 VNC 服务器的详细步骤与配置指南
    在 CentOS 6.5 系统上部署 VNC 服务器时,首先需要确认 VNC 服务是否已安装。通常情况下,VNC 服务默认未安装。可以通过运行特定的查询命令来检查其安装状态。如果查询结果为空,则表明 VNC 服务尚未安装,需进行手动安装。此外,建议在安装前确保系统的软件包管理器已更新至最新版本,以避免兼容性问题。 ... [详细]
  • POJ3669题目解析:基于广度优先搜索的详细解答
    POJ3669(http://poj.org/problem?id=3669)是一道典型的广度优先搜索(BFS)问题。由于陨石的降落具有时间属性,导致地图状态会随时间动态变化。因此,可以利用结构体来记录每个陨石的降落时间和位置,从而有效地进行状态更新和路径搜索。 ... [详细]
  • 汽车电子架构与CAN网络基础解析——鉴源实验室专业解读 ... [详细]
  • 本文详细解析了微信服务端示例类的功能与应用。其中,`ClientResponseHandler` 类主要用于处理微信支付所需的响应数据,而 `TenpayHttpClient` 则是对 HTTP 请求(包括 GET 和 POST 方法)进行了封装,以便在内部调用时更加便捷和高效。这些工具类在实际开发中起到了关键作用,开发者无需深入了解其底层实现细节,即可轻松集成微信支付功能。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • Python 实战:异步爬虫(协程技术)与分布式爬虫(多进程应用)深入解析
    本文将深入探讨 Python 异步爬虫和分布式爬虫的技术细节,重点介绍协程技术和多进程应用在爬虫开发中的实际应用。通过对比多进程和协程的工作原理,帮助读者理解两者在性能和资源利用上的差异,从而在实际项目中做出更合适的选择。文章还将结合具体案例,展示如何高效地实现异步和分布式爬虫,以提升数据抓取的效率和稳定性。 ... [详细]
  • 本文源自极分享,详细内容请参阅原文。技术债务如同信用卡负债,随着时间推移,修复成本会越来越高,因此程序员必须对此有深刻认识。此外,团队应致力于培养一种持续维护和优化代码的文化,以减少技术债务的累积。 ... [详细]
author-avatar
常山他爹没有JJ2000_836
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有