版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
沉浸式状态栏
Google 从 Android kitkat(android 4.4) 开始,给我们开发者提供了一套能透明的系统 UI 样式给状态栏和导航栏,这样的话就可以调整状态栏和导航栏跟 Activity 一样的样式,形成一个完整的主题。
优点:
1.这样设计的好处就是用户可以沉浸到设计的 App 中,没有其他因素可以影响到用户。
2.更符合整体设计风格,用户所看到的一屏 包含 App 内容 状态栏 内容区,导航栏。
3.内容区为开发者所控制,导航栏与状态栏 为本身系统拥有,沉浸式设计就是为导航栏和状态栏展开。
沉浸式设计的目的就是让 App 的效果其能够与手机整体的状态融为一体。不会因为看到其他不和谐的因素而影响用户。
现在实现沉浸式设计主要是修改状态栏和导航栏的背景颜色。
平台版本 API 级别
Android7.0 24 N 平台亮点
Android6.0 23 M 平台亮点
Android 5.1 22 LOLLIPOP_MR1 平台亮点
Android 5.0 21 LOLLIPOP
Android 4.4W 20 KITKAT_WATCH 仅限 KitKat for Wearables
Android 4.4 19 KITKAT 平台亮点
Android 4.3 18 JELLY_BEAN_MR2 平台亮点
Android 4.2、4.2.2 17 JELLY_BEAN_MR1 平台亮点
Android 4.1、4.1.1 16 JELLY_BEAN 平台亮点
Android 4.0.3、4.0.4 15 ICE_CREAM_SANDWICH_MR1 平台亮点
Android 4.0、4.0.1、4.0.2 14 ICE_CREAM_SANDWICH
可以发现,安卓 5.0 以上、安卓 4.4 到 安卓 5.0、安卓 4.4 以下是各不相同的。
安卓 5.0 以上的版本,要进行状态栏的修改有两种方式。
一种是在 style.xml 中设置主题,通过设置 colorPrimaryDark 可以设置状态栏颜色。
style.xml
另一种是安卓在 5.0 以上提供了 setStatusBarColor、setNavigationBarColor 方法分别进行状态栏与导航栏颜色设置,方法使用这个要注意添加版本判断。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
//设置状态栏颜色
getWindow().setStatusBarColor(Color.GREEN);
//设置导航栏颜色
getWindow().setNavigationBarColor(Color.GREEN);
}
**注:**上面方式虽然很方便,但是只能在 5.0 以上使用。
除个别手机厂商对 API 进行修改外,4.4以下是不支持的,所以还是放弃吧。
在安卓 4.4 上,按 5.0 那要进行主题的设置一样是没有效果的。针对这种情况, 4.4 在 style.xml 中也有对应的属性可以设置。
// 将导航栏编程透明
- true
//将状态栏设置成透明
- true
对应的使用代码进行设置的方法:
//必须在 setContentView 之前
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
但是,这种方式只能设置是否透明。
MainActivity :
public class MainActivity extends AppCompatActivity {
Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
setContentView(R.layout.activity_main);
toolbar = findViewById(R.id.toolbar);
toolbar.setTitle("首页");
setSupportActionBar(toolbar);
}
}
activity_main.xml:
style.xml:
单纯的使用 ToolBar,会让 ToolBar 的高度包括了状态栏的高度(ToolBar 中整体高度变大)。状态栏设为透明的时候,是不占空间的。
在布局文件添加 fitsSystemWindows 属性。
activity_main.xml:
这个一个 boolean 值的内部属性,让 view 可以根据系统窗口(如 StatusBar)来调整自己的布局,如果值为true,就会调整 view 的 paingding 属性来给 system windows 留出空间。
实际效果:
当 StatusBar 为透明或半透明时(4.4以上),系统会设置 view 的 paddingTop 值为一个适合的值( status bar的高度)让 view 的内容不被上拉到状态栏,当在不占据 StatusBar 的情况下(4.4以下)会设置 paddingTop 值为0(因为没有占据 StatusBar 所以不用留出空间)。
这种处理方式不仅每个布局都要进行这个属性设置,而且在某些情况下会导致布局异常,特别是在使用 ScrollView 的时候。我们可以根据这个思路来换一种解决方式。
在 ToolBar 上面添加一个状态栏高度的 View。
MainActivity :
public class MainActivity extends BaseActivity {
Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = findViewById(R.id.toolbar);
toolbar.setTitle("首页");
setSupportActionBar(toolbar);
View view = findViewById(R.id.nav);
setToolBarStyle(toolbar, view, Color.GREEN);
}
}
把对状态栏的处理写在 BaseActivity 中,这样所有的 Activity 直接继承即可。
BaseActivity :
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
public void setToolBarStyle(Toolbar toolbar, View topView, int styleColor) {
// 4.4 到 5.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (toolbar != null) {
int statusHeight=getStatusHeight();
Log.i("tuch", " statusHeight " + statusHeight);
//第一种方式
// ViewGroup.LayoutParams layoutParams = topView.getLayoutParams();
// layoutParams.height = statusHeight;
// topView.setBackgroundColor(styleColor);
//第二种
toolbar.setPadding(0,toolbar.getPaddingTop()+statusHeight,0,0)
}
//5.0 以上
}else if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.LOLLIPOP) {
}else {
//没救了
}
}
/**
* 通过反射获取状态栏高度
* @return
*/
private int getStatusHeight() {
int height = -1;
try {
Class> clazz = Class.forName("com.android.internal.R$dimen");
Object object = clazz.newInstance();
String heightStr = clazz.getField("status_bar_height").get(object).toString();
height = Integer.parseInt(heightStr);
//dp--px
height = getResources().getDimensionPixelSize(height);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return height;
}
}
这里有两种方式,第一种是在 ToolBar 上方添加一个 View,通过设置 View 来控制状态栏,包括背景颜色 (推荐这种方式)。第二种是按 fitsSystemWindows 的原理,添加 Padding。
activity_main.xml:
第一种:
第二种:
导航栏处理思路跟状态栏一样,不过有些手机是没有导航栏的,直接是物理按键,所以需要先判断是否有导航栏存在。
BaseActivity :
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
public void setToolBarStyle(Toolbar toolbar,View topView, View bottomView, int styleColor) {
// 4.4 到 5.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (toolbar != null) {
int statusHeight=getStatusHeight();
Log.i("tuch", " statusHeight " + statusHeight);
//第一种方式
ViewGroup.LayoutParams layoutParams = topView.getLayoutParams();
layoutParams.height = statusHeight;
topView.setBackgroundColor(styleColor);
//第二种
// toolbar.setPadding(0,toolbar.getPaddingTop()+statusHeight,0,0)
//下面的导航栏
if (haveNavgtion()) {
ViewGroup.LayoutParams layoutParamsBottom = bottomView.getLayoutParams();
layoutParamsBottom.height = getNavigationHeight();
Log.i("tuch", "getNavigationHeight " + getNavigationHeight());
bottomView.setBackgroundColor(styleColor);
}
}
//5.0 以上
}else if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.LOLLIPOP) {
}else {
//没救了
}
}
/**
* 通过反射获取状态栏高度
* @return
*/
private int getStatusHeight() {
int height = -1;
try {
Class> clazz = Class.forName("com.android.internal.R$dimen");
Object object = clazz.newInstance();
String heightStr = clazz.getField("status_bar_height").get(object).toString();
height = Integer.parseInt(heightStr);
//dp--px
height = getResources().getDimensionPixelSize(height);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return height;
}
/**
* 判断是否有导航栏(API 17 以后才可以)
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
private boolean haveNavgtion() {
//屏幕的高度,真实物理的屏幕
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
display.getRealMetrics(displayMetrics);
int heightDisplay = displayMetrics.heightPixels;
//为了防止横屏
int widthDisplay = displayMetrics.widthPixels;
//显示内容的高度
DisplayMetrics contentDisplaymetrics = new DisplayMetrics();
display.getMetrics(contentDisplaymetrics);
int contentDisplay = contentDisplaymetrics.heightPixels;
int contentDisplayWidth = contentDisplaymetrics.widthPixels;
//屏幕内容高度 显示内容的屏幕
int w = widthDisplay - contentDisplayWidth;
//哪一方大于0 就有导航栏
int h = heightDisplay - contentDisplay;
return w>0||h>0;
}
private int getNavigationHeight() {
int height=-1;
try {
Class> clazz=Class.forName("com.android.internal.R$dimen");
Object object=clazz.newInstance();
String heightStr=clazz.getField("navigation_bar_height").get(object).toString();
height = Integer.parseInt(heightStr);
//dp--px
height = getResources().getDimensionPixelSize(height);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return height;
}
}
activity_main.xml:
效果: