第一种讲解:

在Android系统4.4以前,状态栏的背景色和字体颜色都是不能改变的。但是4.4以后Google增加了改变状态栏背景透明的方法,可以通过两种方式来设置。
直接在Activity中设置Window属性:

@Override
protected void onCreate(Bundle savedInstanceState) {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    }
    super.onCreate(savedInstanceState);
}

在xml的style文件中设置:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">true</item>
</style>

使用android:windowTranslucentStatus属性需要在res目录下新建values-v19文件夹,style文件要放在里面。
尽量使用第一种方式类实现,据说是第二种方式在某些国产手机上没有效果。

两种方式都是让状态背景栏透明。但是在实际测试后发现,在android4.4系统上状态栏的背景是渐变半透明。而在android5.0+的系统上又有所不同又有差异:

在Genymotion模拟器、vivo、nexus6p的android5.0+系统上是半透明。
在xiaomi、oppo、huawei、leshi等5.0+系统是全透明的。

View android 半透明 设置 安卓布局透明_背景色

android4.4

 

View android 半透明 设置 安卓布局透明_状态栏_02

模拟器android5.0+

 

View android 半透明 设置 安卓布局透明_android_03

小米5android6.0

从效果图也可以看出,上面两种方式使得内容区域延伸到状态栏下面去了。我们可以添加android:fitsSystemWindows来避免这样的情况。
在Activity的根布局文件中添加

 

android:fitsSystemWindows="true"

或者在主题style文件中添加

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

 

View android 半透明 设置 安卓布局透明_背景色_04

android4.4

 

View android 半透明 设置 安卓布局透明_状态栏_05

模拟器android5.0+

 

View android 半透明 设置 安卓布局透明_View android 半透明 设置_06

小米5android6.0

上图为了方便观察状态栏的背景颜色,就将Activity的布局文件的背景色设置成了红色。可以看出Toolbar所在的内容区域没有沉浸到状态栏下面了,并且状态栏的背景颜色还是之前一样,只是将内容区域向下偏移了状态栏高度的距离。这是因为fitsSystemWindows属性使得布局的paddingTop被重新改写了(paddingTop增加了状态栏的高度)。
要实现沉浸式的状态栏,其实就是状态栏的背景颜色和Toolbar的颜色一样。那么我们将Activity的背景色改为Toolbar紫色(内容区域的背景颜色设置为白色)看下效果:

View android 半透明 设置 安卓布局透明_android_07

android4.4

 

View android 半透明 设置 安卓布局透明_背景色_08

模拟器android5.0+

 

View android 半透明 设置 安卓布局透明_android_09

小米5android6.0

除了通过fitsSystemWindows这个属性,我们可以自己给在Toolbar的上方添加一个和状态栏高度一样的Veiw,并将这个View的背景色设置成和Toolbar的背景色一样。也可以直接给Toobar设置paddingTop等于状态栏高度值。
为了方便,我一般是采用重写Toolbar,改写它的paddingTop值来实现。

 

/**
 * Created by xiaoyanger on 2017/3/1.
 * 沉浸式、版本兼容的Toolbar,状态栏透明.
 */
public class CompatToolbar extends Toolbar {

    public CompatToolbar(Context context) {
        this(context, null);
    }

    public CompatToolbar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CompatToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setup();
    }

    public void setup() {
        int compatPadingTop = 0;
        // android 4.4以上将Toolbar添加状态栏高度的上边距,沉浸到状态栏下方
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            compatPadingTop = getStatusBarHeight();
        }
        this.setPadding(getPaddingLeft(), getPaddingTop() + compatPadingTop, getPaddingRight(), getPaddingBottom());
    }
    
    public int getStatusBarHeight() {
        int statusBarHeight = 0;
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            statusBarHeight = getResources().getDimensionPixelSize(resourceId);
        }
        Log.d("CompatToolbar", "状态栏高度:" + px2dp(statusBarHeight) + "dp");
        return statusBarHeight;
    }

    public float px2dp(float pxVal) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (pxVal / scale);
    }
}

需要注意的是,网上有很多文章说状态栏的高度是25dp,但实际测试后发现并不是所有的机型都是25dp。使用getStatusBarHeight()可以准确的获取状态栏的高度值,看下获取到高度的日志:

View android 半透明 设置 安卓布局透明_android_10

android4.4模拟器

 

View android 半透明 设置 安卓布局透明_状态栏_11

android6.0模拟器

 

View android 半透明 设置 安卓布局透明_状态栏_12

oppo r9

 

View android 半透明 设置 安卓布局透明_状态栏_13

小米5

各个机型获取到的状态栏高度很多都不一样,所以最好还是重写Toolbar,动态获取到系统状态栏的高度来改写它的上边距。

 

国产的大部分手机在android5.0+的系统都将原生的半透明状态栏改成了全透明,因此通过上面的方式基本达到了沉浸式体验。
其实在原生android5.0+系统上可以通过这个方法使状态栏背景色完全透明:

@Override
protected void onCreate(Bundle savedInstanceState) {
    // 5.0以上系统状态栏透明
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = getWindow();
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(Color.TRANSPARENT);
    }
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

 

View android 半透明 设置 安卓布局透明_背景色_14

模拟器(原生系统)android5.0+

可以将上面两个方法封装到BaseActivity中,以便在各个界面直接调用:

 

protected void setTranslucentStatus() {
    // 5.0以上系统状态栏透明
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = getWindow();
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(Color.TRANSPARENT);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    }
}

通过setTranslucentStatus()方法,4.4的系统状态栏背景色渐变半透明,绝大多数5.0+的系统状态栏背景色全透明(测试时发现有部分华为手机状态栏背景是浅紫色,估计是定制系统给修改了)。
需要注意的是,不管是<item name="android:windowTranslucentStatus">true</item>还是setTranslucentStatus()方法,这两种方式都会使这个设置<itemname="colorPrimaryDark">@color/colorPrimaryDark</item>不会有任何效果。


状态栏的背景色按照上面的方式适配基本上满足沉浸式体验,但是有时候会遇到这样的需求,状态栏和Toolbar背景色都是白底黑字,比如简书消息界面:

 

View android 半透明 设置 安卓布局透明_状态栏_15

简书消息界面Toobar和状态栏白底黑字

 

状态栏的白色背景可以通过上面的方式来实现,但是修改状态栏的文字和图标颜色就比较麻烦了,原生的android系统只有在6.0+才有官方的api,但是在MIUI和FlymeUI提供了相应的方法。

/**
 * 设置Android状态栏的字体颜色,状态栏为亮色的时候字体和图标是黑色,状态栏为暗色的时候字体和图标为白色
 *
 * @param dark 状态栏字体和图标是否为深色
 */
protected void setStatusBarFontDark(boolean dark) {
    // 小米MIUI
    try {
        Window window = getWindow();
        Class clazz = getWindow().getClass();
        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);
        if (dark) {    //状态栏亮色且黑色字体
            extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
        } else {       //清除黑色字体
            extraFlagField.invoke(window, 0, darkModeFlag);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    // 魅族FlymeUI
    try {
        Window window = getWindow();
        WindowManager.LayoutParams lp = window.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 (dark) {
            value |= bit;
        } else {
            value &= ~bit;
        }
        meizuFlags.setInt(lp, value);
        window.setAttributes(lp);
    } catch (Exception e) {
        e.printStackTrace();
    }
    // android6.0+系统
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (dark) {
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
    }
}

状态栏和Toobar白底黑字只能适配到android6.0+以及小米和魅族手机,其他手机的只能调整UI设计了。可以看一下简书和掘金,在小米、魅族和android6.0+的系统上都是白底黑字,但是在其他的系统上一般是将状态栏的背景色设置为浅灰色。

View android 半透明 设置 安卓布局透明_状态栏_16

简书消息界面oppo r9(5.1系统)

其实状态栏显示的颜色是灰色,但是有多种方式来实现,参考下简书、掘金、今日头条,他们都使用了右滑返回上一界面,在滑动过程中就能看出状态背景色的实现。

View android 半透明 设置 安卓布局透明_android_17

简书

 

View android 半透明 设置 安卓布局透明_android_18

掘金

 

View android 半透明 设置 安卓布局透明_android_19

今日头条

简书的效果可以不用上面的方式使内容区域沉浸到状态栏下面,直接使用的<item name="colorPrimaryDark">@color/colorPrimaryDark</item>直接修改的状态栏的颜色。

 

掘金的状态栏是半透明的,内容区域已经沉浸到状态栏下方,可以使用上面的方法,对于不是android6.0+、MIUI、FlymeUI的系统通过window.setStatusbarColor方法将状态栏颜色改为半透明的灰色。

今日头条的效果可以采用上面的方法实现沉浸式,状态栏全透明,Toolbar没有增加paddingTop,而是在第二个界面中状态栏的下方(Toolbar上方)加了一块同状态栏高度一样的View,颜色为灰色。

其实,从上面三个app的状态栏适配效果来看,要达到真正的沉浸式,今日头条的效果要好一些。但是今日头条并没有在各个机型上适配状态栏的字体和图标颜色。
下面自己来实现不同机型的状态栏背景色和字体图标颜色的适配。
首先,新建一个CompatStartusbarActivity,继承自BaseActivity

public class CompatStatusBarActivity extends BaseActivity {

    private FrameLayout mFrameLayoutContent;
    private View mViewStatusBarPlace;

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

        mViewStatusBarPlace = findViewById(R.id.view_status_bar_place);
        mFrameLayoutContent = (FrameLayout) findViewById(R.id.frame_layout_content_place);

        ViewGroup.LayoutParams params = mViewStatusBarPlace.getLayoutParams();
        params.height = getStatusBarHeight();
        mViewStatusBarPlace.setLayoutParams(params);
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        View contentView = LayoutInflater.from(this).inflate(layoutResID, null);
        mFrameLayoutContent.addView(contentView);
    }

    /**
     * 设置沉浸式状态栏
     *
     * @param fontIconDark 状态栏字体和图标颜色是否为深色
     */
    protected void setImmersiveStatusBar(boolean fontIconDark, int statusBarColor) {
        setTranslucentStatus();
        if (fontIconDark) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                    || OsUtil.isMIUI()
                    || OsUtil.isFlyme()) {
                setStatusBarFontIconDark(true);
            } else {
                if (statusBarColor == Color.WHITE) {
                    statusBarColor = 0xffcccccc;
                }
            }
        }
        setStatusBarPlaceColor(statusBarColor);
    }

    private void setStatusBarPlaceColor(int statusColor) {
        if (mViewStatusBarPlace != null) {
            mViewStatusBarPlace.setBackgroundColor(statusColor);
        }
    }


    public int getStatusBarHeight() {
        int statusBarHeight = 0;
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            statusBarHeight = getResources().getDimensionPixelSize(resourceId);
        }
        return statusBarHeight;
    }

    /**
     * 设置状态栏透明
     */
    private void setTranslucentStatus() {
        // 5.0以上系统状态栏透明
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
    }

    /**
     * 设置Android状态栏的字体颜色,状态栏为亮色的时候字体和图标是黑色,状态栏为暗色的时候字体和图标为白色
     *
     * @param dark 状态栏字体是否为深色
     */
    private void setStatusBarFontIconDark(boolean dark) {
        // 小米MIUI
        try {
            Window window = getWindow();
            Class clazz = getWindow().getClass();
            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);
            if (dark) {    //状态栏亮色且黑色字体
                extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
            } else {       //清除黑色字体
                extraFlagField.invoke(window, 0, darkModeFlag);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 魅族FlymeUI
        try {
            Window window = getWindow();
            WindowManager.LayoutParams lp = window.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 (dark) {
                value |= bit;
            } else {
                value &= ~bit;
            }
            meizuFlags.setInt(lp, value);
            window.setAttributes(lp);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // android6.0+系统
        // 这个设置和在xml的style文件中用这个<item name="android:windowLightStatusBar">true</item>属性是一样的
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (dark) {
                getWindow().getDecorView().setSystemUiVisibility(
                        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                                | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            }
        }
    }
}

布局文件activity_compat_status_bar.xml

<?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="com.xiao.compatstatusbar.MainActivity">

    <View
        android:id="@+id/view_status_bar_place"
        android:layout_width="match_parent"
        android:layout_height="25dp"/>

    <FrameLayout
        android:id="@+id/frame_layout_content_place"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

setImmersiveStatusBar方法中中判断了不能修改状态栏字体和图标颜色则将白色的状态栏底色改成灰色,能修改状态栏字体和图标的颜色则改为给定的颜色statusBarColor。使用的时候直接调用这个方法即可。


下面是使用示例:
MainActivity继承自CompatStatusBarActivity

public class MainActivity extends CompatStatusBarActivity {

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_main);
        int color = 0xffaa66cc;
        toolbar.setBackgroundColor(color);
        setImmersiveStatusBar(false, color);
    }

    public void go(View view) {
        startActivity(new Intent(this, NextActivity.class));
    }
}

布局文件activity_main.xml

<?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="com.xiao.compatstatusbar.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar_main"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/colorPrimary">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="标题"
                android:textColor="@android:color/white"
                android:textSize="18sp"/>
        </LinearLayout>
    </android.support.v7.widget.Toolbar>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#99FF4081"
        android:onClick="go"
        android:text="页面1"
        android:textSize="44sp"/>
</LinearLayout>

NextActivity同样继承自CompatStatusBarActivity:

public class NextActivity extends CompatStatusBarActivity {

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_next);
        int color = 0xffffffff;
        toolbar.setBackgroundColor(color);
        setImmersiveStatusBar(true, color);
    }
}

布局文件activity_next.xml

<?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="com.xiao.compatstatusbar.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar_next"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/colorPrimary">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="标题"
                android:textColor="#353535"
                android:textSize="18sp"/>
        </LinearLayout>
    </android.support.v7.widget.Toolbar>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#99FF4081"
        android:text="页面2"
        android:textSize="44sp"/>
</LinearLayout>

最终适配效果:

View android 半透明 设置 安卓布局透明_背景色_20

模拟器android4.4

 

View android 半透明 设置 安卓布局透明_背景色_21

模拟器android5.0

 

View android 半透明 设置 安卓布局透明_背景色_22

模拟器android6.0

 

View android 半透明 设置 安卓布局透明_View android 半透明 设置_23

oppo r9

 

View android 半透明 设置 安卓布局透明_背景色_24

mi5
源码:https://github.com/xiaoyanger0825/CompatStatusBar

 

第二种讲解:

 

首先明确一下沉浸式状态栏的概念有两种类型

  1. 顶部是ImageView这种需要将其填充到状态栏

image.png

  1. 顶部是ActionBar这种不需要填充到状态栏

image.png

而上面的第二种情况覆盖了绝大部分页面,所以下面的思路也是基于这点来实现。


第一步:
在BaseActivity里面实现如下代码

public abstract class BaseActivity extends AppCompatActivity  {

    @Override
    protected final void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStatusBar();
    }

    protected void setStatusBar() {
        //这里做了两件事情,1.使状态栏透明并使contentView填充到状态栏 2.预留出状态栏的位置,防止界面上的控件离顶部靠的太近。这样就可以实现开头说的第二种情况的沉浸式状态栏了 
        StatusBarUtil.setTransparent(this);
    }
}

看下StatusBarUtil.setTransparent(this)具体的实现

/**
     * 设置状态栏全透明
     *
     * @param activity 需要设置的activity
     */
    public static void setTransparent(Activity activity) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return;
        }
        transparentStatusBar(activity);
        setRootView(activity);
    }

    /**
     * 使状态栏透明
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private static void transparentStatusBar(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            //需要设置这个flag contentView才能延伸到状态栏
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            //状态栏覆盖在contentView上面,设置透明使contentView的背景透出来
            activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
        } else {
            //让contentView延伸到状态栏并且设置状态栏颜色透明
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
    }

   /**
     * 设置根布局参数
     */
    private static void setRootView(Activity activity) {
        ViewGroup parent = (ViewGroup) activity.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);
            }
        }
    }

经过这样的设置基本上大部分界面都适配了沉浸式状态栏了,针对需要适配imageView填充到状态栏或者需要单独设置状态栏颜色和透明度的我们可以在BaseActivity 的子类重写setStatusBar方法来单独适配。

第二步:

  1. 先说简单的,单独设置状态栏颜色和透明度这种情况
//重写这个方法
    protected void setStatusBar() {
        StatusBarUtil.setColor(...);
    }

    /**
     * 设置状态栏颜色
     *
     * @param activity       需要设置的activity
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度
     */

    public static void setColor(Activity activity, @ColorInt int color, @IntRange(from = 0, to = 255) int statusBarAlpha) {
        //5.0以上版本直接设置状态栏颜色透明度
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha));
        //4.4-5.0版本通过伪装一个状态栏来设置颜色和透明度
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
            View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
            if (fakeStatusBarView != null) {
                if (fakeStatusBarView.getVisibility() == View.GONE) {
                    fakeStatusBarView.setVisibility(View.VISIBLE);
                }
                fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
            } else {
                decorView.addView(createStatusBarView(activity, color, statusBarAlpha));
            }
            setRootView(activity);
        }
    }

    /**
     * 计算状态栏颜色
     *
     * @param color color值
     * @param alpha alpha值
     * @return 最终的状态栏颜色
     */
    private static int calculateStatusColor(@ColorInt int color, int alpha) {
        if (alpha == 0) {
            return color;
        }
        float a = 1 - alpha / 255f;
        int red = color >> 16 & 0xff;
        int green = color >> 8 & 0xff;
        int blue = color & 0xff;
        red = (int) (red * a + 0.5);
        green = (int) (green * a + 0.5);
        blue = (int) (blue * a + 0.5);
        return 0xff << 24 | red << 16 | green << 8 | blue;
    }

    /**
     * 生成一个和状态栏大小相同的半透明矩形条
     *
     * @param activity 需要设置的activity
     * @param color    状态栏颜色值
     * @param alpha    透明值
     * @return 状态栏矩形条
     */
    private static View createStatusBarView(Activity activity, @ColorInt int color, int alpha) {
        // 绘制一个和状态栏一样高的矩形
        View statusBarView = new View(activity);
        LinearLayout.LayoutParams params =
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
        statusBarView.setLayoutParams(params);
        statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));
        statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID);
        return statusBarView;
    }

    /**
     * 获取状态栏高度
     *
     * @param context context
     * @return 状态栏高度
     */
    public static int getStatusBarHeight(Context context) {
        // 获得状态栏高度
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        return context.getResources().getDimensionPixelSize(resourceId);
    }
  1. imageView这种需要填充到状态栏的情况
//重写这个方法
    protected void setStatusBar() {
        StatusBarUtil.setTransparentForImageView(...);
    }

    /**
     * 为头部是 ImageView 的界面设置状态栏全透明
     *
     * @param activity       需要设置的activity
     * @param needOffsetView 需要向下偏移的 View
     */
    public static void setTransparentForImageView(Activity activity, View needOffsetView) {
        setTranslucentForImageView(activity, 0, needOffsetView);
    }

   /**
     * 为头部是 ImageView 的界面设置状态栏透明
     *
     * @param activity       需要设置的activity
     * @param statusBarAlpha 状态栏透明度
     * @param needOffsetView 需要向下偏移的 View
     */
    public static void setTranslucentForImageView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha,
                                                  View needOffsetView) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return;
        }
        setTransparentForWindow(activity);
        addTranslucentView(activity, statusBarAlpha);
        //虽然imageView需要填充到状态栏,但是上面的文字按钮等控件并不需要填充到状态栏,所以需要设置偏移
        if (needOffsetView != null) {
            Object haveSetOffset = needOffsetView.getTag(TAG_KEY_HAVE_SET_OFFSET);
            if (haveSetOffset != null && (Boolean) haveSetOffset) {
                return;
            }
            ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams();
            layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(activity),
                    layoutParams.rightMargin, layoutParams.bottomMargin);
            needOffsetView.setTag(TAG_KEY_HAVE_SET_OFFSET, true);
        }
    }

    /**
     * 设置透明
     */
    private static void setTransparentForWindow(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
            activity.getWindow()
                    .getDecorView()
                      //两个 flag 要结合使用,表示让应用的主体内容占用系统状态栏的空间
                    .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            activity.getWindow()
                    .setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
    }

    /**
     * 添加半透明矩形条
     *
     * @param activity       需要设置的 activity
     * @param statusBarAlpha 透明值
     */
    private static void addTranslucentView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) {
        ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
        View fakeTranslucentView = contentView.findViewById(FAKE_TRANSLUCENT_VIEW_ID);
        if (fakeTranslucentView != null) {
            if (fakeTranslucentView.getVisibility() == View.GONE) {
                fakeTranslucentView.setVisibility(View.VISIBLE);
            }
            fakeTranslucentView.setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0));
        } else {
            contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha));
        }
    }

    /**
     * 创建半透明矩形 View
     *
     * @param alpha 透明值
     * @return 半透明 View
     */
    private static View createTranslucentStatusBarView(Activity activity, int alpha) {
        // 绘制一个和状态栏一样高的矩形
        View statusBarView = new View(activity);
        LinearLayout.LayoutParams params =
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
        statusBarView.setLayoutParams(params);
        statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0));
        statusBarView.setId(FAKE_TRANSLUCENT_VIEW_ID);
        return statusBarView;
    }

最后小结一下思路:

首先在基类设置状态栏透明,让contentView填充到状态栏,利用contentView的背景作为状态栏背景直接实现沉浸的效果,然后利用setFitsSystemWindows(true)和setClipToPadding(true)对控件预留出状态栏的位置。

如果需要单独设置状态栏颜色或者透明度,比如contentView的背景色和ActionBar背景不一致的情况,5.0以上通过设置setStatusBarColor实现,4.4-5.0通过伪装状态栏实现。

最后如果需要实现类似图片这种需要填充到状态栏的情况,通过setTransparentForWindow实现,然后对其他控件重设MarginLayoutParams来实现预留出状态栏位置的效果。

经过上面的一些设置已经覆盖了绝大部分情况,对于Fragment,DrawerLayout,CoordinatorLayout等可能需要特殊处理的这里贴上gitgub地址。ps:作者不是我,我只是个搬砖的。
https://github.com/laobie/StatusBarUtil

 

详情可以查看https://www.jianshu.com/p/a44c119d6ef7https://www.jianshu.com/p/fc5854895a10