【Android基础】StatusBar重新认识

前言:

工作几年下来,处理statusbar的机会和场景不算多,每次遇到具体业务问题时,都是去网上找一些api解决当前问题,但是从来没有从根本上去了解statusbar的发展历程,今天下定决心去彻底认识statusbar。

了解statusbar的发展需要分三个阶段:

  1. Android4.4(API19 KitKat)以前:无法做任何事,是的,就是一坨黑色。
  2. Android4.4~Android5.0(API21 Lollipop):可以实现状态栏的变色,但是效果还不是很好,主要实现方式是通过FLAG_TRANSLUCENT_STATUS这个属性设置状态栏为透明并且为全屏模式,然后通过添加一个与StatusBar 一样大小的View,将View 设置为我们想要的颜色,从而来实现状态栏变色。
  3. Android5.0~Android6.0(API23 Marshmallow): 系统才真正的支持状态栏变色,系统加入了一个重要的属性和方法 android:statusBarColor (对应方法为 setStatusBarColor),通过这个属性可以直接设置状态栏的颜色
  4. Android6.0以后:主要就是添加了一个功能可以修改状态栏上内容和图标的颜色(黑色和白色)

下面开始了解主要的API:
一、首先新建一个项目,注意把values/values-v19/values-v21中凡是在style.xml中 有关 windowTranslucentNavigation、windowTranslucentStatus、statusBarColor 都删掉。 同时把布局带有android:fitsSystemWindows注释掉。

public class Main3Activity extends AppCompatActivity{
    
    

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

}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".Main3Activity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textColor="@android:color/holo_red_light"
        android:text="测试文案"
        />

    <View
        android:id="@+id/testview"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@color/colorPrimary"
        tools:ignore="MissingConstraints" />
</LinearLayout>

跑出来的效果是:
从左到右分别是 API 19(4.4), 21(5.0), 29(9.+)
在这里插入图片描述
二、代码中设置FLAG_TRANSLUCENT_STATUS后,请看如下效果:

public class Main3Activity extends AppCompatActivity{
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    }
}

4.4会全透明,页面上顶。
模拟器4.4应该是有bug,看不出效果,建议使用真机
在这里插入图片描述
4.4以上是一样的效果,页面整体上顶,沉在状态栏之下,同时状态栏变为透明!
下图左边是 api21, 右边是api29, 以下相同
在这里插入图片描述
这里我们给出一个结论:
android4.4—android5.0主要通过FLAG_TRANSLUCENT_STATUS这个属性实现状态栏变色,当使用这个flag时SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN会被自动添加。

这显然不是我们想要的效果,我们试着让状态栏透明,同时页面不被状态栏遮挡
三、我们使用setFitsSystemWindows(android:fitsSystemWindows=“true”)这个api试试。

public class Main3Activity extends AppCompatActivity{
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        ViewGroup rootView = (ViewGroup) ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
        rootView.setFitsSystemWindows(true);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true"    //注意是这里!
    tools:context=".Main3Activity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textColor="@android:color/holo_red_light"
        android:text="测试文案"
        />

    <View
        android:id="@+id/testview"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@color/colorPrimary"
        tools:ignore="MissingConstraints" />
</LinearLayout>
或者在XML局部中设置:

在这里插入图片描述
在这里插入图片描述

四、4.4没有提供改变statusbar颜色的方案,我们采取了一个hack的方式。
方法是:要添加一个view填充在状态栏上,view的高度就是状态栏的高度,颜色就是你想要的状态栏的颜色。

public class Main3Activity extends AppCompatActivity{
    
    
    private static final int FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    
    
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            ViewGroup decorView = (ViewGroup) getWindow().getDecorView();
            View fakeStatusBarView = decorView.findViewById(R.id.statusbarutil_fake_status_bar_view);
            if (fakeStatusBarView != null) {
    
    
                if (fakeStatusBarView.getVisibility() == View.GONE) {
    
    
                    fakeStatusBarView.setVisibility(View.VISIBLE);
                }
                fakeStatusBarView.setBackgroundColor(getResources().getColor(R.color.colorAccent));
            } else {
    
    
                decorView.addView(createStatusBarView(getResources().getColor(R.color.colorAccent)));
            }
            setRootView();
        }
    }

    private void setRootView() {
    
    
        ViewGroup parent = (ViewGroup) findViewById(android.R.id.content);
        for (int i = 0, count = parent.getChildCount(); i < count; i++) {
    
    
            View childView = parent.getChildAt(i);
            if (childView instanceof ViewGroup) {
    
    
                childView.setFitsSystemWindows(true);
                ((ViewGroup) childView).setClipToPadding(true);
            }
        }
    }

    private View createStatusBarView( @ColorInt int color) {
    
    
        // 绘制一个和状态栏一样高的矩形
        View statusBarView = new View(this);
        LinearLayout.LayoutParams params =
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight());
        statusBarView.setLayoutParams(params);
        statusBarView.setBackgroundColor(color);
        statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID);
        return statusBarView;
    }

    private int getStatusBarHeight() {
    
    
        // 获得状态栏高度
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        return getResources().getDimensionPixelSize(resourceId);
    }
}

在这里插入图片描述
注意:以下分析的api都与4.4无关了,4.4能做的事情就是对添加到顶部的view进行UI的变化,自主定制,思路都是一致的。

五、5.0(包含5.0)以上,设置statusbar的颜色可以直接利用一个window提供的api即可,不需要做任何限制。
注意:写改代码时一定要判断sdk版本,不加判断的话,运行到5.0以下机器上会直接crash

public class Main3Activity extends AppCompatActivity{
    
    
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    
    
			getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            // 上面三行代码,如果设置为全透明,则必须加上,设置为半透明,则可以不需要
            getWindow().setStatusBarColor(getResources().getColor(R.color.colorAccent));
        }
    }
}

在这里插入图片描述

六、如果实现导航栏半透明,图片作为布局底部,沉在导航栏下的效果。

public class Main3Activity extends AppCompatActivity{
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    
    
            // 设置状态栏透明
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
    }
}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".Main3Activity">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:src="@drawable/brand_pic_kr"
        android:scaleType="centerCrop"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textColor="@android:color/holo_red_light"
        android:text="测试文案"
        />

    <View
        android:id="@+id/testview"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@color/colorPrimary"
        tools:ignore="MissingConstraints" />
</LinearLayout>

在这里插入图片描述

七、改变状态栏文字颜色:
Android 6.0 以上使用系统方法修改状态栏字体、图标颜色;
Android 4.4 到 6.0 之间使用第三方系统提供的方法修改状态栏字体、图标颜色(目前只有 MIUI 和 Flyme)。

  setMIUIStatusBarDarkIcon(activity, true);
  setMeizuStatusBarDarkIcon(activity, true);
//设置为黑色
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    
    
  activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | 	View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
//设置为白色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    
    
    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}


    /**
     * 修改 MIUI V6  以上状态栏颜色
     */
    private static void setMIUIStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) {
    
    
        Class<? extends Window> clazz = activity.getWindow().getClass();
        try {
    
    
            Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
            Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
            int darkModeFlag = field.getInt(layoutParams);
            Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
            extraFlagField.invoke(activity.getWindow(), darkIcon ? darkModeFlag : 0, darkModeFlag);
        } catch (Exception e) {
    
    
            //e.printStackTrace();
        }
    }

    /**
     * 修改魅族状态栏字体颜色 Flyme 4.0
     */
    private static void setMeizuStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) {
    
    
        try {
    
    
            WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
            Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
            Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");
            darkFlag.setAccessible(true);
            meizuFlags.setAccessible(true);
            int bit = darkFlag.getInt(null);
            int value = meizuFlags.getInt(lp);
            if (darkIcon) {
    
    
                value |= bit;
            } else {
    
    
                value &= ~bit;
            }
            meizuFlags.setInt(lp, value);
            activity.getWindow().setAttributes(lp);
        } catch (Exception e) {
    
    
            //e.printStackTrace();
        }
    }

API29状态栏文字设置为黑色的效果:
在这里插入图片描述

八、FLAG_FULLSCREEN的作用:

public class Main3Activity extends AppCompatActivity{
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    
    
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }
    }
}

分别为api19,21,29
在这里插入图片描述
使状态栏消失,比如小说阅读APP中可以使用到。

不得不提一句,除此以外,还有很多个flag,其实很多api功能重叠,或者因为版本问题遗留,导致该问题的处理上没有统一方案,大家找到适合自己的就好。

九、实现类似横屏视频播放的效果,默认不展示状态栏,触摸屏幕时显示出来状态栏。
参考:https://blog.csdn.net/sbsujjbcy/article/details/48391863

十、基本原理弄清楚以后,其实我们在实际使用中,可以推荐一个牛逼的轮子:
https://jaeger.itscoder.com/android/2016/03/27/statusbar-util.html

猜你喜欢

转载自blog.csdn.net/yearningseeker/article/details/108769697
今日推荐