第一种讲解:

在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+系统是全透明的。

android4.4

模拟器android5.0+

小米5android6.0

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

android:fitsSystemWindows="true"

或者在主题style文件中添加

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

android4.4

模拟器android5.0+

小米5android6.0

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

android4.4

模拟器android5.0+

小米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()可以准确的获取状态栏的高度值,看下获取到高度的日志:

android4.4模拟器

android6.0模拟器

oppo r9

小米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);
}

模拟器(原生系统)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背景色都是白底黑字,比如简书消息界面:

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

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

/*** 设置Android状态栏的字体颜色,状态栏为亮色的时候字体和图标是黑色,状态栏为暗色的时候字体和图标为白色** @param dark 状态栏字体和图标是否为深色*/
protected void setStatusBarFontDark(boolean dark) {// 小米MIUItry {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();}// 魅族FlymeUItry {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+的系统上都是白底黑字,但是在其他的系统上一般是将状态栏的背景色设置为浅灰色。

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

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

简书

掘金

今日头条

简书的效果可以不用上面的方式使内容区域沉浸到状态栏下面,直接使用的<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;@Overrideprotected 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);}@Overridepublic 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) {// 小米MIUItry {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();}// 魅族FlymeUItry {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"?>
<LinearLayoutxmlns: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"><Viewandroid:id="@+id/view_status_bar_place"android:layout_width="match_parent"android:layout_height="25dp"/><FrameLayoutandroid: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 {@Overrideprotected 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"?>
<LinearLayoutxmlns: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.Toolbarandroid:id="@+id/toolbar_main"android:layout_width="match_parent"android:layout_height="50dp"android:background="@color/colorPrimary"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid: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><TextViewandroid: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 {@Overrideprotected 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"?>
<LinearLayoutxmlns: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.Toolbarandroid:id="@+id/toolbar_next"android:layout_width="match_parent"android:layout_height="50dp"android:background="@color/colorPrimary"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid: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><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="#99FF4081"android:text="页面2"android:textSize="44sp"/>
</LinearLayout>

最终适配效果:

模拟器android4.4

模拟器android5.0

模拟器android6.0

oppo r9

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

第二种讲解:

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

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

    image.png

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

    image.png

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


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

public abstract class BaseActivity extends AppCompatActivity  {@Overrideprotected 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/a44c119d6ef7或https://www.jianshu.com/p/fc5854895a10

Android实现沉浸式(透明)状态栏适配相关推荐

  1. Android 沉浸式(透明)状态栏细研-超级细还附 Demo

    前言 在 Android 4.4 中引入了沉浸模式的功能,但这个版本非真正的沉浸模式,应该说是透明模式.Android 5.0 以后才可以在系统层面实现真正的沉浸式状态栏. 沉浸式状态栏是为了与当前使 ...

  2. Android UI体验之全屏沉浸式透明状态栏效果

    前言: Android 4.4之后谷歌提供了沉浸式全屏体验, 在沉浸式全屏模式下, 状态栏. 虚拟按键动态隐藏, 应用可以使用完整的屏幕空间, 按照 Google 的说法, 给用户一种 身临其境 的体 ...

  3. android滑动背景变透明,Android右滑退出+沉浸式(透明)状态栏

    背景 上篇文章一个千万量级的APP使用的一些第三方库中,在说到一个使用很广泛的滑动退出库SwipeBackLayout时有提过有时间会分享自己在项目中引入这个库的时候填过的一些坑.前段时间项目加入沉浸 ...

  4. Android 沉浸式透明状态栏与导航栏

    Android 系统自4.2 开始 UI 上就没多大改变,4.4 也只是增加了透明状态栏与导航栏的功能 这个特性是andorid4.4支持的,最少要api19才可以使用.下面介绍一下使用的方法,非常得 ...

  5. Android实现沉浸式状态栏(透明状态栏)(QQ和简书样式)

    本文内容大多参考网络上的Android实现沉浸式状态栏教程,由于看了太多类似文章,固在此不再一一列举出处.本文仅为我自己梳理思路和供各位看到本文的朋友参考(百度:Android沉浸式状态栏,第一页内容 ...

  6. Android一键沉浸式状态栏(透明状态栏)

    文章适合新手了解Android一键沉浸式状态栏.文章在编写过程中难免有疏漏和错误,欢迎大佬指出文章的不足之处:更多内容请点进

  7. Android各种沉浸式状态栏实现

    1.引入 dependencies {compile 'com.gyf.barlibrary:barlibrary:2.3.0' } 2.使用:在BaseActivity 中初始化 public ab ...

  8. Android设置状态栏颜色,沉浸式状态栏 fllutter Android设置沉浸式状态栏

    1. Android原生设置沉浸式状态栏,设置状态栏颜色,字体颜色 import android.annotation.TargetApi; import android.app.Activity; ...

  9. Android实现沉浸式状态栏的那些坑

    最近项目需要实现沉浸式的状态栏,其实我在之前就了解过Android的沉浸式,发现有些棘手就放弃了,但是此次是公司的项目需要的,就花了几天把这个问题搞定了,在此记录一下,并mark几个坑. 首先,沉浸式 ...

最新文章

  1. 某程序员吐槽:前端开发被哄抢,专科学历能进大厂;客户端开发找工作难如登天,985本硕拿不到一个offer!...
  2. Android数据库专家秘籍(七)经验LitePal查询艺术
  3. Java基础看jvm,JAVA基础知识|java虚拟机(JVM)
  4. linux gone kde 图形程序 兼容,Oracle8i HowTo
  5. php中的控制器是什么意思,理解PHP中的MVC编程之控制器_php
  6. 用python画图代码-【Matplotlib】利用Python进行绘图
  7. bzoj 4953: [Wf2017]Posterize(DP)
  8. 4:JAVA UUID 生成
  9. 【原】ajaxupload.js上传报错处理方法
  10. 180720_有道词典离线增强版添加词库(小众知识)
  11. 动作捕捉用于索并联机构中的理论验证
  12. 程序设计作业——系统管理
  13. jolog扫地机器人怎么样_扫地机器人怎么样?
  14. (day12)css3基础
  15. IOS实现点击软键盘的Next/Return按钮或者空白处后自动隐藏键盘
  16. AGV移动机器人PID运动控制
  17. java保证一段代码枷锁_Java堆外内存之突破JVM枷锁
  18. 教育知识与能力(1)
  19. 亚马逊html怎么上传,亚马逊 帮助: 如何上传“在线试读”文件
  20. 阿里云mysql 连接数_阿里云mysql最大连接数

热门文章

  1. 微信支付--上传图片
  2. vue 项目dev/sit/uat环境判断
  3. ip视频语音对讲系统是否可以用在方舱医院?
  4. Ubuntu 10.10 ThinkPad x200 指点杆移动鼠标速度调整
  5. 【linux】循序渐进学运维-基础篇-Linux系统启动原理
  6. MMR自动摘要 python实现
  7. mycat 跨库间连表查询解决方案
  8. c#环境下的surfer等值线制图开发
  9. MATLAB如何把数据写入文本txt文件
  10. 实际问题中提出一个检验统计量,如何确定其分布?