A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 翁志雄Gson 中级黑马   /  2016-5-6 10:51  /  13145 人查看  /  64 人回复  /   3 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 翁志雄Gson 于 2016-5-6 11:09 编辑


1. 概念
首先让我们看一下默认状态栏的显示效果 以下以微信与qq为例:


由于Android 系统自4.2 开始 UI 上就没多大改变,而4.4增加了透明状态栏与导航栏的功能如以下图的效果。
从以上的图可以看出ActivitycontentView( setContentView设置的视图)可以延伸到statusBar下方 这种体验就叫沉浸式体验。这个是从用户的角度来感受的。如果从设计师的角度来看就是「Immersive Mode 全浸模式」/「Translucent Bars 半透明状态栏」。不同共同的特点就是“将布局内容延伸到手机状态栏”。

对于这种显示效果又有以下两种模式:
a.全屏( ContentView 可以进入状态栏)
b.非全屏 ( ContentView 与状态栏分离, 状态栏直接着色

2. 案例分析

2.1. sdk5.x以上  

准备5.0模拟器
2.1.1. 非全屏幕:theme默认主题着色状态栏
根据5.0以上的theme配置要求。只需配置colorPirmary(标题栏颜色),colorPrimaryDark(手机状态栏颜色)即可.

① 在values/styles.xml创建主题

<resources>
    <!--应用的基本主题 -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- 自定义应用主题 -->
        <!--标题颜色-->
        <item name="colorPrimary">@color/colorPrimary</item>
        <!--状态栏颜色-->
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <!--text /checkBox颜色-->
        <item name="colorAccent">@color/colorAccent</item>
    </style>
</resources>


② 在systembar/src/main/AndroidManifest.xml引用
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <!-这里是activity ->
</application>

运行效果
2.1.2. 非全屏幕:Javacode着色状态栏
代码位置:com.example.systembar.CodeActivity
① 先写版本判断
② 直接调用setStatusBarColor

Activity activity = this;
int color=Color.parseColor("#008000");
//针对版本5.x以上的即LOLLIPOP以上的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    //非全屏幕修改状态栏颜色
    Window window = activity.getWindow();
    //FLAG_TRANSLUCENT_STATUS为状态栏类型:半透明效果
    //取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    //需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
    //FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS为状态栏类型:支持着色
   window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    //设置状态栏颜色  setStatusBarColor要求21以上
    window.setStatusBarColor(color);
    ViewGroup contentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
    View childView = contentView.getChildAt(0);//对应activity布局文件根标签
    if (childView != null) {
    //注意不是设置 ContentView 的 FitsSystemWindows, 而是设置 ContentView 的第一个子 View . 预留出系统 View 的空间.
        ViewCompat.setFitsSystemWindows(childView, true);
    }
}

运行效果:根据标题颜色着色

l android:fitsSystemWindows/setFitsSystemWindows

所有实现 Translucent System Bar 效果的Activity,都需要在根布局里设置 android:fitsSystemWindows="true" 。设置了该属性的作用在于,不会让系统导航栏和我们app的UI重叠,导致交互问题.以下给出效果图说明该属性。

2.1.3. 全屏幕:contentview延伸
代码位置:com.example.systembar.CodeFullActivity#onCreate

//去掉titlebar-全屏模式
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
//细节supportRequestWindowFeature一定要在setContentView之前设置
setContentView(R.layout.activity_code_full);
Activity activity = this;
int statusColor = Color.parseColor("#008000");
//针对版本5.x以上的即LOLLIPOP以上的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    Window window = activity.getWindow();
    //设置透明状态栏,这样才能让 ContentView 向上
    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    //需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    //设置状态栏颜色
    window.setStatusBarColor(statusColor);
    ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
    View mChildView = mContentView.getChildAt(0);
    if (mChildView != null) {
        //注意不是设置 ContentView 的 FitsSystemWindows, 而是设置 ContentView 的第一个子 View .
        // 使其不为系统 View 预留空间.不预留空间的话 状态栏就会覆盖布局顶部
        ViewCompat.setFitsSystemWindows(mChildView, false);
    }
}

运行效果



2.2. 4.4以上
准备4.4模拟器

5.x以上的实现方式不使4.4以上的生效.所以要考虑一下兼容方案。


2.2.1. 全屏幕:contentview延伸
思路:
暂时保存标题栏时可以清楚得看到只要将contentView延伸到statusbar下方.它与statusBar共同放在一个FrameLayout里。

代码位置:com.example.systembar.CodeFullActivity#onCreate
① 获取系统statusBar的高度

private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height";
public static int getStatusBarHeight(Context context) {
    int result = 0;
    //访问android.jar内的dimen中包含的status_bar_height属性值
    int resourceId = context.getResources().getIdentifier(STATUS_BAR_HEIGHT_RES_NAME, "dimen", "android");
    if (resourceId > 0) {
        result = context.getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

② 设置无标题栏
//去掉titlebar-全屏模式
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
//细节supportRequestWindowFeature一定要在setContentView之前设置
setContentView(R.layout.activity_code_full);

③ 设置 contentView的marginTop上移状态栏高度

//KITKAT为4.4sdk的名称
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    Window window = activity.getWindow();
    ViewGroup contentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
    //首先使 ChildView 不预留空间
    View childView = contentView.getChildAt(0);//layout file对应的根标签
    if (childView != null) {
        //此时status会遮盖contnetview
        ViewCompat.setFitsSystemWindows(childView, false);
    }
    int statusBarHeight = getStatusBarHeight(activity);
    //需要设置这个 flag 才能设置状态栏
    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    //避免多次调用该方法时,多次移除了 View
    if (childView != null && childView.getLayoutParams() != null && childView.getLayoutParams().height == statusBarHeight) {
        //移除假的 View.
        contentView.removeView(childView);
        childView = contentView.getChildAt(0);
    }
    if (childView != null) {
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) childView.getLayoutParams();
        //清除 ChildView 的 marginTop 属性
        if (lp != null && lp.topMargin >= statusBarHeight) {
            //减少layout的topmargin 使用layout布局的整体延伸到statusBar下方
            lp.topMargin -= statusBarHeight;
            childView.setLayoutParams(lp);
        }
    }
}

Deug下可以查看到


l activity.findViewById(Window.ID_ANDROID_CONTENT);获取的是存放layout的FrameLayout具体类型为ContentFrameLayout
l Layout的内容可以从 contentView.getChildAt(0);获取。
l 在状态栏透明的情况下 lp.topMargin -= statusBarHeight;可以使用layout上移

运行效果

2.2.2. 非全屏幕:Javacod着色状态栏
① 获取系统状态栏高度(同上)
② 在状态栏透明情况下下移contnetView 距离为状态栏高度
③ 如果存在着色View则修改背景色
④ 如果不存在着色View则添加同时设置背景色

//判断当前sdk是4.4  KITKAT为4.4sdk的名称
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        Window window = activity.getWindow();
        ViewGroup contentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
        //设置状态栏透明
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        //获取状态栏高度
        int statusBarHeight = getStatusBarHeight(activity);
        //在全屏模式下 使用contentView内容下移状态栏高度
        View childView = contentView.getChildAt(0);
        if (childView != null) {
            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) childView.getLayoutParams();
            //如果已经为 ChildView 设置过了 marginTop, 再次调用时直接跳过
            if (lp != null && lp.topMargin < statusBarHeight && lp.height != statusBarHeight) {
                //不预留系统空间
                ViewCompat.setFitsSystemWindows(childView, false);
                lp.topMargin += statusBarHeight;//在状态栏透明的情况下 contentView marginTop改为状态栏高度
                childView.setLayoutParams(lp);
            }
        }
       //下移的状态栏高度  已经存在着色View直接设置color
        View statusBarView = contentView.getChildAt(0);
        if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == statusBarHeight) {
            //避免重复调用时多次添加已经着色的View
            statusBarView.setBackgroundColor(statusColor);
        }else
        {
            //如果未存在刚添加着色View下次通过contentView.getChildAt(0);即为该着色View
            statusBarView = new View(activity);
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight);
            statusBarView.setBackgroundColor(statusColor);
            //向 ContentView 中添加已经着色的View
            contentView.addView(statusBarView, 0, lp);
        }
        
    }
}

运行效果
我在此处添加的着色View背景为绿色
此时还可以去掉系统自带的标题栏
【源代码】

2.3. 开源第三方SystemBarTint
下面介绍一个将以上过程封装比较到位的第三方开源状态栏工具库
支持4.4以上
Github地址
运行效果
4.4以上
5.x以上


2016-05-03_233550.png (53.29 KB, 下载次数: 222)

2016-05-03_233550.png

2016-05-04_000017.png (69.68 KB, 下载次数: 206)

2016-05-04_000017.png

64 个回复

正序浏览
多谢分享
回复 使用道具 举报
学习一下。。。。。。
回复 使用道具 举报
前来围观。。。
回复 使用道具 举报
newu 黑马帝 2016-7-29 19:45:09
61#
支持支持支持支持支持~~~
回复 使用道具 举报
syso 中级黑马 2016-7-29 17:30:18
60#
赞赞赞赞赞赞赞赞赞赞
回复 使用道具 举报
好久没来论坛了
回复 使用道具 举报
来此阔以原地复活加满血,能量杠杠滴,哈哈
回复 使用道具 举报
climb 中级黑马 2016-5-12 19:47:48
57#
来熟悉熟悉页面。。。以后好
回复 使用道具 举报
小伙伴们加油~ 希望来论坛里朋友都能拿到行业高薪~需要Android资源的可以到站内资源库去取 http://bbs.itheima.com/thread-300073-1-1.html
回复 使用道具 举报
学习了,翁总威武
回复 使用道具 举报
QQ空间那种沉浸式图片的实现原理可以简单描述一下吗?5.0的系统是不是也得需要单独处理
回复 使用道具 举报
但是沉浸式顶栏在5.0系统之后,状态栏改为半透明色,与4.4系统实现沉浸式顶栏还有区别
回复 使用道具 举报
Mi123 中级黑马 2016-5-11 08:38:09
52#
学习了,翁总威武
回复 使用道具 举报
向楼主学习!
回复 使用道具 举报
黑马币........
回复 使用道具 举报
wkong 中级黑马 2016-5-10 21:59:26
49#
可以。。。。
回复 使用道具 举报
zjf1992 发表于 2016-5-10 00:23
看起来真的很高深.

其实只要使用最后的第三方库。两行代码就可以啦
回复 使用道具 举报
看起来真的很高深.
回复 使用道具 举报
顶雄哥。。。。。。
回复 使用道具 举报
厉害厉害~~
回复 使用道具 举报
1234下一页
您需要登录后才可以回帖 登录 | 加入黑马