一步两步带你实现Android沉浸式设计

Posted 花花young

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一步两步带你实现Android沉浸式设计相关的知识,希望对你有一定的参考价值。

前言

       沉浸式不知道什么时候有了两种叫法,一种是沉浸式模式,一种是沉浸式状态栏,Google从android4.4开始,给我们开发者提供了一套透明的系统UI样式给状态栏和导航栏,这样完美的玩法简直和ios系统媲美了。


Part 1、沉浸式模式

    

public void toggle(View view) 
        int options = getWindow().getDecorView().getSystemUiVisibility();
        if (Build.VERSION.SDK_INT >= 14) 
            options ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;//隐藏导航条
        
        if (Build.VERSION.SDK_INT >= 16) 
            options ^= View.SYSTEM_UI_FLAG_FULLSCREEN;//隐藏系统栏
        
        if (Build.VERSION.SDK_INT >= 19) 
            //这里有两种沉浸模式
            //SYSTEM_UI_FLAG_IMMERSIVE_STICKY 你在系统栏区域滑动使得系统栏显示为半透明,
            // 但是你的flag并没有被清除,监听没有被触发,这个系统栏会自动隐藏

            //SYSTEM_UI_FLAG_IMMERSIVE 你在系统栏区域滑动使得系统栏显示,将会保持可见的状态
            options ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        
        getWindow().getDecorView().setSystemUiVisibility(options);
    

如果你想给单个设置,只需要按照下面的参数进行设置即可

getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE  //稳定布局
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //隐藏导航栏
                        | View.SYSTEM_UI_FLAG_FULLSCREEN //隐藏系统栏
                        | View.SYSTEM_UI_FLAG_IMMERSIVE);


Part 2沉浸式状态栏

在Android5.0+自动实现了沉浸式的效果,状态栏的颜色跟随你在主题中的colorPrimaryDark属性,通过样式进行修改

        <item name="android:statusBarColor">@color/system_bottom_nav_color</item>
在代码中进行设置
        getWindow().setStatusBarColor(getResources().getColor(R.color.material_blue_grey_800));
然而对于沉浸式状态栏要做到兼容的效果着实不易,现在最低兼容到4.4以上,可以在style文件进行设置
        <item name="android:windowTranslucentStatus">true</item>
但这种方法并不推荐使用,兼容性不好,在代码中设置
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

似乎设置起来很简单,但是上面的两种会出现严重的bug,状态栏遮挡住了ToolBar

    

面对这样的情况我们快速想到的就是加上android:fitsSystemWindows="true",那么在哪层布局加呢?首先我们先来说一下这个属性的含义:设置布局时,是否考虑当前系统窗口的布局,如果为true就会调整整个系统窗口布局(包括状态栏的view)以适应你的布局,不妨我们尝试一下给ToolBar添加android:fitsSystemWindows="true"

效果~

    

然而ToolBar向上移填充了状态栏部分,这也验证了android:fitsSystemWindows="true"只是让系统窗口布局去适应你设置的控件,针对上面这种效果提供了两种解决方案

1、<推荐>将android:fitsSystemWindows="true"放在最外层的容器,或者你也可以在代码中进行设置

ViewGroup contentView = (ViewGroup) findViewById(Window.ID_ANDROID_CONTENT);//得到屏幕不包括标题栏的区域
        View rootView = contentView.getChildAt(0);//得到xml的根布局
        if(rootView != null && Build.VERSION.SDK_INT >= 14)
            rootView.setFitsSystemWindows(true);
        

效果~

    

发现给布局最外层容器设置android:fitsSystemWindows="true" 可以达到状态栏透明,并且露出底色---android:windowBackground颜色,这时候需要直接将最外层容器(也可以修改-android:windowBackground颜色)设置成状态栏想要的颜色,下面剩下的布局再包裹一层正常的背景颜色。

效果~

    

这种解决方案不仅可以解决了状态栏遮挡ToolBar的问题,还解决了ScrollView+EditText存在时ToolBar会被顶出去的问题

效果~

    

2、针对状态栏遮挡ToolBar的问题还有另外一种解决方案,就是去掉在ToolBar设置的fitsSystemWindows,增加ToolBar的高度并且为ToolBar设置padding,值得注意是此方案也没有解决ScrollView+EditText会将ToolBar推上去的问题

        //1.先设置toolbar的高度
        ViewGroup.LayoutParams params = mToolbar.getLayoutParams();
        int statusBarHeight = getStatusBarHeight(this);
        params.height += statusBarHeight ;
        mToolbar.setLayoutParams(params );
设置ToolBar的padding

        //2.设置paddingTop,以达到状态栏不遮挡toolbar的内容。
        mToolbar.setPadding(
                mToolbar.getPaddingLeft(),
                mToolbar.getPaddingTop()+getStatusBarHeight(this),
                mToolbar.getPaddingRight(),
                mToolbar.getPaddingBottom());

通过查看android.R.dimen.status_bar_height文件知道需要通过反射来得到状态栏的高度

    public int getStatusBarHeight(Context context) 
        int statusBarHeight = 0;
        try 
            Class<?> clazz = Class.forName("com.android.internal.R$dimen");
            Object obj = clazz.newInstance();
            Field field = clazz.getField("status_bar_height");
            int temp = Integer.parseInt(field.get(obj).toString());
            statusBarHeight = context.getResources().getDimensionPixelSize(temp);
         catch (Exception e) 
            e.printStackTrace();
        
        return statusBarHeight;
    
效果~

    


Part 3、沉浸式虚拟导航栏

在Android5.0+实现底部导航沉浸效果,style实现

<item name="android:navigationBarColor">@color/colorPrimary_pink</item>
代码实现
getWindow().setNavigationBarColor()
在Android4.4可以使用特殊手段让导航栏设置为透明颜色,style实现
<item name="android:windowTranslucentNavigation">true</item>

代码实现

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
效果~

    

然而虚拟导航栏遮挡了内容,回头想想可能会想到这种情况和上面状态栏遮挡toolbar是一个问题,上面有两种解决方案但对于虚拟导航栏只能使用增加高度设置padding来解决,但是要如何去解决呢,底部的导航栏却不能像ToolBar一样能获得相应的View。这里我们可以做一个假象,让它下面有个设置为和底部虚拟导航栏高度一样的View并设置背景。开始搞~

1、在布局文件底部定义一个View

    <View
        android:id="@+id/navigationview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@color/colorPrimary"/>
2、在代码中进行动态的设置
        ViewGroup.LayoutParams pras = mNavigationbar.getLayoutParams();
        int navigationBarHeight = getNavigationBarHeight(this);
        pras.height += navigationBarHeight ;
        mNavigationbar.setLayoutParams(pras);
    

    private int getNavigationBarHeight(Context context) 
        int navigationbarBarHeight = 0;
        try 
            Class<?> clazz = Class.forName("com.android.internal.R$dimen");
            Object obj = clazz.newInstance();
            Field field = clazz.getField("navigation_bar_height");
            int temp = Integer.parseInt(field.get(obj).toString());
            navigationbarBarHeight = context.getResources().getDimensionPixelSize(temp);
         catch (Exception e) 
            e.printStackTrace();
        
        return navigationbarBarHeight;
    
效果~

    

这样似乎已经解决了导航栏沉浸的效果,但还差点什么,那就是如何判断手机是否有虚拟导航栏或者是否开启了虚拟导航栏,这里说一种常用的思路就是通过判断屏幕的高度-内容的高度>0则存在虚拟导航栏。

	private static boolean hasNavigationBarShow(WindowManager wm)
		Display display = wm.getDefaultDisplay();
		DisplayMetrics outMetrics = new DisplayMetrics();
		//获取整个屏幕的高度
		display.getRealMetrics(outMetrics);
		int heightPixels = outMetrics.heightPixels;
		int widthPixels = outMetrics.widthPixels;
		//获取内容展示部分的高度
		outMetrics = new DisplayMetrics();
		display.getMetrics(outMetrics);
		int heightPixels2 = outMetrics.heightPixels;
		int widthPixels2 = outMetrics.widthPixels;
		int w = widthPixels-widthPixels2;
		int h = heightPixels-heightPixels2;
		return  w>0||h>0;//竖屏和横屏两种情况。
	
至此,我们就可以写出一套兼容性的沉浸式状态栏导航栏了。
if(android.os.Build.VERSION.SDK_INT>=android.os.Build.VERSION_CODES.KITKAT
				&&android.os.Build.VERSION.SDK_INT<android.os.Build.VERSION_CODES.LOLLIPOP)
			if(toolbar!=null)
				LayoutParams params = toolbar.getLayoutParams();
				int statusBarHeight = getStatusBarHeight(this);
				params.height += statusBarHeight ;
				toolbar.setLayoutParams(params );
				toolbar.setPadding(
						toolbar.getPaddingLeft(),
						toolbar.getPaddingTop()+getStatusBarHeight(this), 
						toolbar.getPaddingRight(),
						toolbar.getPaddingBottom());
				toolbar.setBackgroundColor(translucentPrimaryColor);
			
			if(bottomNavigationBar!=null)
				if(hasNavigationBarShow(getWindowManager()))
					LayoutParams p = bottomNavigationBar.getLayoutParams();
					p.height += getNavigationBarHeight(this);
					bottomNavigationBar.setLayoutParams(p);
					bottomNavigationBar.setBackgroundColor(translucentPrimaryColor);
				
			
		else if(android.os.Build.VERSION.SDK_INT>=android.os.Build.VERSION_CODES.LOLLIPOP)
			getWindow().setNavigationBarColor(translucentPrimaryColor);
			getWindow().setStatusBarColor(translucentPrimaryColor);
		else
			//<4.4的,不做处理
		












以上是关于一步两步带你实现Android沉浸式设计的主要内容,如果未能解决你的问题,请参考以下文章

一步一步带你入门MySQL中的索引和锁

一步一步带你入门MySQL中的索引和锁 (转)

一步一步带你体验算法之魅力

一步一步带你体验算法之魅力

一步一步带你体验算法之魅力

一步一步带你体验算法之魅力