从设置Android透明状态栏和深色模式了解Activity的结构View

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/g777520/article/details/80695296

前言

开始,想写一个设置状态栏的通用类,之后又需要结合小米和6.0之后状态栏字体变为黑色等,就找了些资料。
一下子就实现了功能,觉得也挺快的。然后做了一些就想了解一下一个Activity的布局到底有那些view,因为,我们对一些没有放出接口的方法,只能用反射才能获取,然后再设置属性。所以想着就了解一下Activity的View布局


做一个简单的状态栏设置通用类

开始都是copy代码,所以很简单的设置了以下方法。

这边加入使用的测试手机是 红米2

这边是只考虑了api19的情况,就先放简单的代码

  • 设置状态栏透明化
    public static void setStatusBarTranslucent(Activity activity){
        Window window = activity.getWindow();
        if (Build.VERSION.SDK_INT >= 19) {
            //透明状态栏
            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            //透明导航栏
            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }
    }
  • 获取状态栏高度
    public static int getStatusBarHeight(Context context){
        int barId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        return context.getResources().getDimensionPixelSize(barId);
    }
  • 添加状态栏view
    public static void addStatusBarView(Activity activity){
        View view = new View(activity);
        view.setBackgroundColor(Color.RED);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                getStatusBarHeight(activity));
        ViewGroup group = activity.findViewById(android.R.id.content);
        group.addView(view, lp);
    }
  • 设置小米状态栏黑色字体
    public static boolean setMIUIBar(Activity activity, boolean dark){
        Window window = activity.getWindow();
        if (window != null) {
            Class clazz = window.getClass();
            try {
                int modeFlag;
                Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
                Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
                modeFlag = field.getInt(layoutParams);
                Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);

                if (dark) {
                    //透明状态栏 黑色字体
                    extraFlagField.invoke(window, modeFlag, modeFlag);
                }else {
                    //白色字体
                    extraFlagField.invoke(window, 0, modeFlag);
                }

                //开发版 7.7.13及以后版本采用了安卓6.0系统API
                setBarMode(activity, dark);

                return true;
            }catch (Exception e){

            }
        }
        return false;
    }
  • 设置6.0以上状态栏深色字体
    public static void setBarMode(Activity activity, boolean dark){
        View decorView = activity.getWindow().getDecorView();
        if (Build.VERSION.SDK_INT >= 23) {
            if (dark) {
                decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            }else {
                decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            }
        }
    }

然后我们设置到Activity上面

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.e("-s-", "sdk = "+ Build.VERSION.SDK_INT);

        StatusBarUtils.setStatusBarTranslucent(this);

        StatusBarUtils.addStatusBarView(this);

        StatusBarUtils.setMIUIBar(this, true);
    }

效果如下

  1. 在xml布局中的效果
    xml布局中的效果

  2. 实际效果
    实际效果

  3. 发现好像被自带的ActionBar给挡住了,我们到style中加入一个没有ActionBar的设置之后
    NoActionBar

  4. 之后我们在activity_main中最外层布局加入android:fitsSystemWindows="true"
    fitsSystemWindows=true

到目前为止我们确实就完成了设置状态栏的背景颜色,修改状态栏的字体了!

本来这样就结束了,之后也就稍微写规范点就可以实现通用类的设置了。
但是,之后想看看到底Activity底下的View到底是什么。


研究Activity主View

从上面的设置我可以知道两个很有用的信息。
1.android.R.id.content
2.getWindow().getDecorView()

这两个到底代表什么呢
我对代码做了如下修改

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.e("-s-", "sdk = "+ Build.VERSION.SDK_INT);

//        StatusBarUtils.setStatusBarTranslucent(this);
//
//        StatusBarUtils.addStatusBarView(this);
//
//        StatusBarUtils.setMIUIBar(this, true);

        ViewGroup decorView = (ViewGroup) getWindow().getDecorView();
        Log.e("-s-", "decorView getChildCount = "+ decorView.getChildCount());

        for (int i = 0; i < decorView.getChildCount(); i++) {
            Log.e("-s-", "getClassName = "+ decorView.getChildAt(i).getClass().getName());
        }

        ViewGroup content = findViewById(android.R.id.content);
        Log.e("-s-", "content getChildCount = "+ content.getChildCount());

        for (int i = 0; i < content.getChildCount(); i++) {
            Log.e("-s-", "getClassName = "+ content.getChildAt(i).getClass().getName());
        }

    }
}

观察到如下log

E/-s-: sdk = 19
E/-s-: decorView getChildCount = 1
E/-s-: getClassName = android.widget.LinearLayout
E/-s-: content getChildCount = 1
E/-s-: getClassName = android.support.constraint.ConstraintLayout

我查了下百度

一、DecorView为整个Window界面的最顶层View。

二、DecorView只有一个子元素为LinearLayout。代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域。

三、LinearLayout里有两个FrameLayout子元素。

原文 Android DecorView浅析

为了看下实际我也打开了Android Device Monitor查看
布局排序

大体上我们就可以通过上面的布局知道了两个的关联了。

而且我们开始设置的时候,发现添加BarView是在android.R.id.content中添加一个高度和默认状态栏一样高度的view,所以我就试验一下取消addStatusBarView的注释

单独添加barview

E/-s-: sdk = 19
E/-s-: decorView getChildCount = 1
E/-s-: getClassName = android.widget.LinearLayout
E/-s-: content getChildCount = 2
E/-s-: getClassName = android.support.constraint.ConstraintLayout
E/-s-: getClassName = android.view.View

之后我把setStatusBarTranslucent也打开了,然后就是正常的设置了状态栏的颜色。
然后我又做了添加多个addStatusBarView,发现确实添加了多个barview。

至此我们可以确认,状态栏是单独一个由系统控制好了的,然后我们修改状态栏的颜色其实就是设置flag将系统默认的状态栏view去掉。然后让布局能够整个填充,然后我们在content 这个view中加入新的barview和原来的setContentView(R.layout.activity_main)之后我们在未最外层添加一个android:fitsSystemWindows="true"属性让setContentView加入的view能够在content已经添加好的view之下,就实现了修改状态栏颜色。

大概就是这样吧!

大概变化

为了证实 我做了如下实验
我不设置最外层的fitsSystemWindows属性,而是为最外层添加一个topMargin

修改代码如下

        for (int i = 0; i < content.getChildCount(); i++) {
            Log.e("-s-", "getClassName = "+ content.getChildAt(i).getClass().getName());
//            content.getChildAt(0).setFitsSystemWindows(true);

            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) content.getChildAt(0).getLayoutParams();
            lp.topMargin = StatusBarUtils.getStatusBarHeight(this);
            content.getChildAt(0).setLayoutParams(lp);
        }

最后的结果却是和设置fitsSystemWindows属性一模一样!



总结

用了这么久,以前也知道DecorView和content,只是没这么深入的了解一下。趁着这次设置就实地的深入学习了下。


参考资料

白底黑字!Android浅色状态栏黑色字体模式
Android DecorView浅析

猜你喜欢

转载自blog.csdn.net/g777520/article/details/80695296