MaterialDesign--(8)沉浸式设计

572 阅读4分钟

什么是沉浸式?

沉浸式这个概念提出来很久了,相信大家也都知道是什么。是什么我不啰嗦了,反正玩来玩去就是在玩状态栏的颜色。

我记得 MaterialDesign 系列的第一篇文章就给大家分享了一张图。


对,没错,我们今天要讲的就是图上的StatusBarColor。沉浸式也就是在玩这个 StatuBarColor,其实没什么意思。 如 全民TV 的首页设计

WechatIMG10.png
WechatIMG10.png

怎么用

getWindow().setStatusBarColor(int color); 好了,讲完了。

兼容性问题

刚刚这个方法仅适用与5.0+的手机,如果要兼容5.0以下的手机,则状态栏只支持透明效果,但是没有 api 提供去设置颜色。
4.4以前的手机不支持修改状态栏颜色
现在我们就来看看怎么修改4.4~5.0的状态栏颜色。
首先我们的,系统中有一个 windowTranslucentStatus 属性,不知道大家用过没,就是设置状态栏为半透明,并且会被 View 填充。然后我们的思路就是,抽取出 TranslucentActivity,然后提供方法,让 Toolbar 填充状态栏。
第一步,在 values-v19/style.xml 文件里面给 style 添加下面这条属性

<item name="android:windowTranslucentStatus">true</item>

第二步,使用继承了这个 Theme 的 的主题给 Activity。 第三步,抽取 BaseTranslucentActivity

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class BaseTranslucentActivity extends AppCompatActivity {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //判断版本,如果[4.4,5.0)就设置状态栏和导航栏为透明
    if(android.os.Build.VERSION.SDK_INT>=android.os.Build.VERSION_CODES.KITKAT
            &&android.os.Build.VERSION.SDK_INT<android.os.Build.VERSION_CODES.LOLLIPOP){
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        //设置虚拟导航栏为透明
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
    }
}

@SuppressLint("NewApi")
public void setOrChangeTranslucentColor(Toolbar toolbar,int translucentPrimaryColor){
    //判断版本,如果[4.4,5.0)就设置状态栏和导航栏为透明
    if(android.os.Build.VERSION.SDK_INT>=android.os.Build.VERSION_CODES.KITKAT
            &&android.os.Build.VERSION.SDK_INT<android.os.Build.VERSION_CODES.LOLLIPOP){
        if(toolbar!=null){
            //1.先设置toolbar的高度
            LayoutParams params = toolbar.getLayoutParams();
            int statusBarHeight = getStatusBarHeight(this);
            params.height += statusBarHeight ;
            toolbar.setLayoutParams(params );
            //2.设置paddingTop,以达到状态栏不遮挡toolbar的内容。
            toolbar.setPadding(
                    toolbar.getPaddingLeft(),
                    toolbar.getPaddingTop()+getStatusBarHeight(this),
                    toolbar.getPaddingRight(),
                    toolbar.getPaddingBottom());
            //设置顶部的颜色
            toolbar.setBackgroundColor(translucentPrimaryColor);
        }else if(android.os.Build.VERSION.SDK_INT>=android.os.Build.VERSION_CODES.LOLLIPOP){
        getWindow().setNavigationBarColor(translucentPrimaryColor);
        getWindow().setStatusBarColor(translucentPrimaryColor);
    }else{
        //<4.4的,不做处理
    }
}


private int getNavigationBarHeight(Context context) {
    return getSystemComponentDimen(this, "navigation_bar_height");
}

/**
 * 获取状态栏的高度
 * @param context
 * @return
 */
private int getStatusBarHeight(Context context) {
    // 反射手机运行的类:android.R.dimen.status_bar_height.
    return getSystemComponentDimen(this, "status_bar_height");
}

private static int getSystemComponentDimen(Context context, String dimenName){
    // 反射手机运行的类:android.R.dimen.status_bar_height.
    int statusHeight = -1;
    try {
        Class<?> clazz = Class.forName("com.android.internal.R$dimen");
        Object object = clazz.newInstance();
        String heightStr = clazz.getField(dimenName).get(object).toString();
        int height = Integer.parseInt(heightStr);
        //dp--->px
        statusHeight = context.getResources().getDimensionPixelSize(height);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return statusHeight;
}

private static boolean hasNavigationBarShow(WindowManager wm){
    Display display = wm.getDefaultDisplay();
    DisplayMetrics outMetrics = new DisplayMetrics();
    //获取整个屏幕的高度
    display.getRealMetrics(outMetrics);
    int heightPixels = outMetrics.heightPixels;
    int widthPixels = outMetrics.widthPixels;
    //获取内容展示部分的高度
    outMetrics = new DisplayMetrics();
    display.getMetrics(outMetrics);
    int heightPixels2 = outMetrics.heightPixels;
    int widthPixels2 = outMetrics.widthPixels;
    int w = widthPixels-widthPixels2;
    int h = heightPixels-heightPixels2;
    return  w>0||h>0;//竖屏和横屏两种情况。
}
}

代码里面的注释清晰明了,简单粗暴。宝宝在做这一块兼容性开发的时候踩过很多很多的坑,查了很多资料,最后才有了这个类。使用反射机制获取代码中状态栏的高度,然后再给 Toolbar 设置 PaddingTop,然后就 Ok 了。 当然,你也可以用 fitsSystemWindows 为true 来,实现,那不那个需要给每个activity 的 xml 都设置这个属性,多麻烦,哈哈哈~

第二个问题:可能很多同学都会有这样的场景需求,我们需要一个全透明的状态栏覆盖在图片上方,如下图

WechatIMG8.jpeg
WechatIMG8.jpeg

但是实际上却变成了这样,半透明

WechatIMG9.png
WechatIMG9.png

妈卖批,这是为什么,我明明设置了 windowTranslucentStatus 为 true,系统控件可以填充状态栏,然后再把状态栏设置为透明色啊。 后来,终于在一篇博客中找到了解决方案~~

    if (Build.VERSION.SDK_INT >= 21) {
        View decorView = getWindow().getDecorView();
        int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
        decorView.setSystemUiVisibility(option);
        getWindow().setStatusBarColor(Color.TRANSPARENT);
    }

不用设置 windowTranslucentStatus 为 true,直接使用这两个 flag 就好了,不会的同学赶紧做笔记~~

问题解决了,那么这些 flag 到底有什么作用呢,我们直接点击这个 flag 就可以看源码,源码里面都有英语注释的,下面是我统计好的一些 flag 的作用。

参数 api 版本 含义
View.SYSTEM_UI_FLAG_VISIBLE 14 默认标记
View.SYSTEM_UI_FLAG_LOW_PROFILE 14 低功耗模式, 会隐藏状态栏图标, 在4.0上可以实现全屏
View.SYSTEM_UI_FLAG_LAYOUT_STABLE 16 保持整个View稳定, 常跟bar 悬浮, 隐藏共用, 使View不会因为SystemUI的变化而做layout
View.SYSTEM_UI_FLAG_FULLSCREEN 16 状态栏隐藏
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 16 状态栏上浮于Activity
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 14 隐藏导航栏
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 16 导航栏上浮于Activity
View.SYSTEM_UI_FLAG_IMMERSIVE 19 Kitkat新加入的Flag, 沉浸模式, 可以隐藏掉status跟navigation bar, 并且在第一次会弹泡提醒, 它会覆盖掉之前两个隐藏bar的标记, 并且在bar出现的位置滑动可以呼出bar
View.SYSTEM_UI_FLAG_IMMERSIVE_STIKY 19 与上面唯一的区别是, 呼出隐藏的bar后会自动再隐藏掉

然后这里设置的 flag 会和WindowManger 的一些 flag相互影响,如果使用的过程中,出现了预测之外的效果,可以去看看源码注释,上面有说明哪些 flag 会影响到哪些 flag,或许能解决问题。

沉浸式牵涉到的知识点也就这些,有什么问题留言吧~

上周五有了新需求,所以这篇文章晚来了今天,Sorry~