app中的沉浸式
官方的沉浸式的含义是指,APP沉浸(充斥了整个屏幕)在屏幕里面,没有显示状态栏,甚至没有显示底部导航栏。
如:
- 全屏状态下的视频播放器
- 全屏状态下的阅读器
类似这样:
而我们一般所说的沉浸式是顶部状态栏和标题栏颜色一致,或者为一张图片。
- 状态栏为图片延伸(4.4版本效果)
- 状态栏为纯颜色(4.4版本效果)
- 导航栏和状态栏颜色一致(4.4版本效果)
支持版本:
- 5.0+版本
- 4.4以上版本
4.4以下版本是无法做到沉浸式,除非修改底层。
5.0+版本的实现方式
5.0+可以自动实现了沉浸式效果,状态栏的颜色跟随你的主题里面的colorPrimaryDark属性,相对来说非常简单,我们这里有三种方法:
在此之前,我们需要把样式设为NoActionBar
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
一、状态栏
1、设置style 文件中 colorPrimary 和 colorPrimaryDark的颜色值一样
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimary</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
2、设置style 文件中 属性样式 android:statusBarColor 的颜色值
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:statusBarColor">@color/colorAccent</item>
</style>
3、通过代码设置,在setContentView之 后。getWindow().setStatusBarColor();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().setNavigationBarColor(Color.GREEN);
}
4、如果像设置图片在状态栏中
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
//并且,需要动态计算statusbar的高度,设置进去。
这里下面再讲
二、底部导航栏
1、在属性文件中,设置android:navigationBarColor的颜色值。
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:navigationBarColor">@color/colorAccent</item>
</style>
2、通过代码设置,同样是在setContentView之后
getWindow().setNavigationBarColor(int color);
5.0以上,都有专门提供的api,所以没什么难度,大家看看就行,选一种自己觉得方便的方法就行,我们着重讲一下,如何兼容4.4版本。
4.4+版本
首先,状态栏,我们可以分别在属性样式和代码中进行设置。
1、在属性样式里(不推荐,需要建values-v19)
<item name="android:windowTranslucentStatus">true</item>
2、在代码中
记住在 setContentView 之前设置。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
setContentView(R.layout.activity_main);
然后发现会出现了副作用。
解决方法一:
app的内容顶到最上面了,即状态栏遮挡一部分界面。
这是因为,把状态栏掩藏掉,那部分高度被app顶上去了,如何解决?,我们只需要在布局文件中,这里以toolbar为例,设置android:fitsSystemWindows=”true” 。
解决。
fitsSystemWindows这是什么意思呢?,他的意思是说,让view可以根据系统窗口(如statusBar)来调整自己的布局,如果值为true,就会调整view的paingding属性来给systemWindows留出空间。
我们看看第二个解决办法。
解决方法二:
系统文件的状态栏高度在sdk\platforms\android-23\data\res\values\demins.xml中我们可以找到系统文件中的状态栏高度。
<!-- Height of the status bar -->
<dimen name="status_bar_height">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_height">48dp</dimen
默认为24dp,当然不同的手机厂商可能高度不一样,我们不能写死,需要动态获取这个值。
这里有两种思路,一个是反射,一个是通过getResources.getIdentifier()。
这里都写一遍出来。
通过反射
public int getStatusBarHeight(){
int statusHeight=-1;
try {
Class<?> aClass = Class.forName("com.android.internal.R$dimen");
Object o = aClass.newInstance();
Field status_bar_height = aClass.getField("status_bar_height");
int statusBarHeightId = (int) status_bar_height.get(o);
// 获取的其实是资源id 需要转换成px
statusHeight = getResources().getDimensionPixelSize(statusBarHeightId);
} catch (Exception e) {
e.printStackTrace();
}
}
return statusHeight==-1?dp2px(24):statusHeight;
}
通过getResources.getIdentifier()
public int getStatusBarHeight(){
int statusBarId = getResources().getIdentifier("status_bar_height", "dimen", "android");
int statusBarHeight=-1;
if(statusBarId>0){
statusBarHeight = getResources().getDimensionPixelSize(statusBarId);
}
//代码默认为24dp
return statusBarHeight==-1?dp2px(24):statusBarHeight;
}
获取到状态栏的高度之后,我们是不是可以把Toolbar的高度增加相同的高度,app顶到状态的问题不就可以轻松解决了。
ViewGroup.LayoutParams params = toolbar.getLayoutParams();
params.height+=getStatusBarHeight();
titleBar.setLayoutParams(params);
titleBar.setPadding(toolbar.getPaddingLeft(),
toolbar.getPaddingRight(),
toolbar.getPaddingTop()+getStatusBarHeight(),
toolbar.getPaddingBottom()
);
接着,4.4版本的导航条
1、显示导航条,并且自定义颜色。
因为4.4并没有提供对应的方法,这里的思路是,让它透明,然后再布局中写一个高度一样的view,置底,重合navigationBar的位置。
首先,计算navigationBar的高度,这个和状态栏statusBar是一样的。
反射
public int getNavigationBarHeight(){
int navigationHeight=-1;
try {
Class<?> aClass = Class.forName("com.android.internal.R$dimen");
Object o = aClass.newInstance();
Field status_bar_height = aClass.getField("navigation_bar_height");
int statusBarHeightId = (int) status_bar_height.get(o);
// 获取的其实是资源id 需要转换成px
navigationHeight = getResources().getDimensionPixelSize(statusBarHeightId);
} catch (Exception e) {
e.printStackTrace();
}
}
return navigationHeight==-1?dp2px(24):navigationHeight;
}
getResources.getIdentifier()
//获取底部的导航条的高度
public int getNavigationBarHeight(){
int navigationId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
int navigationBaHeight=-1;
if(navigationId>0){
navigationBaHeight = getResources().getDimensionPixelSize(navigationId);
}
//代码默认为48dp
return navigationBaHeight==-1?dp2px(48):navigationBaHeight;
}
我们的xml布局
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
app:title="测试"
app:titleTextColor="#fff"
android:background="@color/colorAccent"
></android.support.v7.widget.Toolbar>
<View
android:layout_alignParentBottom="true"
android:id="@+id/nivagtion"
android:layout_width="match_parent"
android:layout_height="0dp"/>
</RelativeLayout>
View是关键,这里我们把它置底,并且让他的高度尽可能的小,接着我们重设他的高度
View nivagtionBar = findViewById(R.id.nivagtion);
nivagtionBar.getLayoutParams();
ViewGroup.LayoutParams bootomParams = nivagtionBar.getLayoutParams();
//这里需要 判断是否存在,不存在,4.4设置会报错,同时,设置nivagtionBar的高度为0
if(isBootomBarShow()){
bootomParams.height+=getNavigationBarHeight();
bootomBar.setBackgroundColor(primaryColor);
bootomBar.setLayoutParams(bootomParams);
}else{
bootomParams.height=0;
nivagtionBar.setLayoutParams(bootomParams);
}
//判断是否打开底部导航条 5.1以上没有导航栏也可以设置颜色。
public boolean isBootomBarShow(){
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics=new DisplayMetrics();
//获取整个屏幕的高度
display.getRealMetrics(outMetrics);
int realHeight = outMetrics.heightPixels;
int realWidth = outMetrics.widthPixels;
//获取内容展示的高度
outMetrics=new DisplayMetrics();
display.getMetrics(outMetrics);
int contentHeight = outMetrics.heightPixels;
int contentWidth = outMetrics.widthPixels;
int h = realHeight - contentHeight;
int w = realWidth - contentWidth;
return h>0||w>0;//竖屏情况和横屏情况。
}
上效果图:
2、不显示导航条。
这个比较简单,直接上代码,这是一个可以藏显示导航条的开关方法。
public void toggleHideyBar() {
int uiOptions = getWindow().getDecorView().getSystemUiVisibility();
int newUiOptions = uiOptions;
boolean isImmersiveModeEnabled =
((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions);
if (isImmersiveModeEnabled) {
Log.d(TAG,"打开");
} else {
Log.d(TAG,"关闭");
}
if (Build.VERSION.SDK_INT >= 14) {
newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
if (Build.VERSION.SDK_INT >= 16) {
newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
if (Build.VERSION.SDK_INT >= 18) {
newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
}
效果图:
稍后我会把代码封装好的上传到github。
github已更新,地址:https://github.com/livesun/translucent
比较凌乱的代码分享:
public class BaseTranslucentActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//判断当前版本如果大于4.4 并且小于5.0
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT
&&Build.VERSION.SDK_INT<Build.VERSION_CODES.LOLLIPOP
){
//设置状态栏透明
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//设置导航栏透明
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
/**
*
* @param titleBar 顶布局
* @param bootomBar 导航条
* @param primaryColor 背景颜色
* @param resId 图片
*/
public void setOrChangeTranslucentColor(View titleBar,@Nullable View bootomBar,int primaryColor,int resId){
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT
&&Build.VERSION.SDK_INT<Build.VERSION_CODES.LOLLIPOP
){
//顶部
if(titleBar!=null){
ViewGroup.LayoutParams params = titleBar.getLayoutParams();
params.height+=getStatusBarHeight();
titleBar.setLayoutParams(params);
titleBar.setPadding(titleBar.getPaddingLeft(),
titleBar.getPaddingRight(),
titleBar.getPaddingTop()+getStatusBarHeight(),
titleBar.getPaddingBottom()
);
if(primaryColor!=Color.TRANSPARENT){
titleBar.setBackgroundColor(primaryColor);
}else{
titleBar.setBackground(getResources().getDrawable(resId));
}
}
//底部
if(bootomBar!=null){
ViewGroup.LayoutParams bootomParams = bootomBar.getLayoutParams();
if(isBootomBarShow()){
bootomParams.height+=getNavigationBarHeight();
bootomBar.setBackgroundColor(primaryColor);
bootomBar.setLayoutParams(bootomParams);
}else{
bootomParams.height=0;
bootomBar.setLayoutParams(bootomParams);
}
}
toggleHideyBar();
//5.0
}else if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){
//如果颜色为透明,则用图片
if(primaryColor== Color.TRANSPARENT){
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
ViewGroup.LayoutParams params = titleBar.getLayoutParams();
params.height+=getStatusBarHeight();
titleBar.setLayoutParams(params);
titleBar.setPadding(titleBar.getPaddingLeft(),
titleBar.getPaddingRight(),
titleBar.getPaddingTop()+getStatusBarHeight(),
titleBar.getPaddingBottom()
);
}
if(bootomBar!=null){
getWindow().setNavigationBarColor(primaryColor);
}
getWindow().setStatusBarColor(primaryColor);
toggleHideyBar();
}
}
//获取顶部状态栏的高度
public int getStatusBarHeight(){
int statusBarId = getResources().getIdentifier("status_bar_height", "dimen", "android");
int statusBarHeight=-1;
if(statusBarId>0){
statusBarHeight = getResources().getDimensionPixelSize(statusBarId);
}
//代码默认为24dp
return statusBarHeight==-1?dp2px(24):statusBarHeight;
}
//获取底部的导航条的高度
public int getNavigationBarHeight(){
int navigationId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
int navigationBaHeight=-1;
if(navigationId>0){
navigationBaHeight = getResources().getDimensionPixelSize(navigationId);
}
//代码默认为48dp
return navigationBaHeight==-1?dp2px(48):navigationBaHeight;
}
//判断是否打开底部导航条 5.1以上没有导航栏也可以设置颜色。
public boolean isBootomBarShow(){
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics=new DisplayMetrics();
//获取整个屏幕的高度
display.getRealMetrics(outMetrics);
int realHeight = outMetrics.heightPixels;
int realWidth = outMetrics.widthPixels;
//获取内容展示的高度
outMetrics=new DisplayMetrics();
display.getMetrics(outMetrics);
int contentHeight = outMetrics.heightPixels;
int contentWidth = outMetrics.widthPixels;
int h = realHeight - contentHeight;
int w = realWidth - contentWidth;
return h>0||w>0;//竖屏情况和横屏情况。
}
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,getResources().getDisplayMetrics());
}
public void toggleHideyBar() {
int uiOptions = getWindow().getDecorView().getSystemUiVisibility();
int newUiOptions = uiOptions;
boolean isImmersiveModeEnabled =
((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions);
if (isImmersiveModeEnabled) {
} else {
}
if (Build.VERSION.SDK_INT >= 14) {
newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
if (Build.VERSION.SDK_INT >= 16) {
newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
if (Build.VERSION.SDK_INT >= 18) {
newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
}
}