沉浸式状态栏StatusBar

这篇文章更多是一个总结别人的学习成果,如有冒犯请联系本人立即删除。

使用了文章内容的链接如下:
Android 沉浸式状态栏攻略 让你的状态栏变色吧(鸿洋)
Android状态栏微技巧,带你真正理解沉浸式模式(郭霖)
Android App 沉浸式状态栏解决方案
Android踩坑记之沉浸式StatusBar
Anroid沉浸式状态栏
android全屏/沉浸式状态栏下,各种键盘挡住输入框解决办法(郭霖推荐文章)

1、至于为什么叫沉浸式状态栏可以看这里 为什么在国内会有很多用户把「透明栏」(Translucent Bars)称作 「沉浸式顶栏」?(我发觉基本上每篇说statusbar都会有我自己也传送了;哈哈)
2、沉浸式状态栏仅适用于 4.4以上系统;而4.4至5.0 到 5.0以上这里又分情况

安卓系统上的系统元素

沉浸式状态栏StatusBar_第1张图片
Paste_Image.png

将状态栏和ActionBar隐藏

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View decorView = getWindow().getDecorView();
        int option = View.SYSTEM_UI_FLAG_FULLSCREEN;
        //设置系统UI元素的可见性。其中,SYSTEM_UI_FLAG_FULLSCREEN表示全屏的意思,也就是会将状态栏隐藏。
        decorView.setSystemUiVisibility(option);
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
                //将actionBar隐藏
                actionBar.hide();
        }
    }
}

上面代码效果如下图:

沉浸式状态栏StatusBar_第2张图片
Paste_Image.png

将状态栏透明

//5.0及以上系统才支持
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            View decorView = getWindow().getDecorView();
//两个Flag必须要结合在一起使用,表示会让应用的主体内容占用系统状态栏的空间
    int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
    decorView.setSystemUiVisibility(option);
//将状态栏设置成透明色
    getWindow().setStatusBarColor(Color.TRANSPARENT);
}
ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
                //将actionBar隐藏
                actionBar.hide();
        }

效果:

沉浸式状态栏StatusBar_第3张图片
Paste_Image.png

这里有个问题就是我们的应用如果有标题栏(或toolbar);标题栏就会被系统的状态栏遮盖着一部分。

如下图:

沉浸式状态栏StatusBar_第4张图片
Paste_Image.png

标题栏一部分与状态栏重叠了。解决方法其实就是:

fitsSystemWindows="true"

给 ContentView 的下的ViewGroup设置使其为系统 View 预留空间.

setFitsSystemWindows属性

void setFitsSystemWindows (boolean fitSystemWindows)
Sets whether or not this view should account for system screen decorations such as the status bar and inset its content; that is, controlling whether the default implementation of fitSystemWindows(Rect) will be executed. See that method for more details.

Note that if you are providing your own implementation of fitSystemWindows(Rect), then there is no need to set this flag to true -- your implementation will be overriding the default implementation that checks this flag.

大概意思是:setFitsSystemWindows用来设置影响系统的工具栏如状态栏,决定了这个view是否插入到它的ContentView。当您设置了fitSystemWindows(Rect)而没有将setFitsSystemWindows设置为true,你对fitSystemWindows(Rect)设置是无效的.
该属性可以设置是否为系统 View 预留出空间,当设置为 true 时,会预留出状态栏的空间。

另外一种方式设置透明状态栏

 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            //需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            //状态栏透明颜色
            activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
        }
//为了设置全屏
ViewGroup mContentView = (ViewGroup) activity.findViewById(android.R.id.content);
View mChildView = mContentView.getChildAt(0);
if (mChildView != null) {
    //注意不是设置 ContentView 的 FitsSystemWindows, 而是设置 ContentView 的第一个子 View . 使其不为系统 View 预留空间.
    ViewCompat.setFitsSystemWindows(mChildView, true);
}

注意最后那行代码:

ViewCompat.setFitsSystemWindows(mChildView, true);

这里等同刚刚说的 setFitsSystemWindows(true);,只不过上面的作向下兼容。

true 时,会预留出状态栏的空间。切记!切记!切记!

其实如果你的布局是整个图片的话就不需要true了。false也可以,true主要系为了那些有标题栏的布局。防止状态栏与标题栏部分重叠。

贴贴鸿祥大神写的提示

Paste_Image.png

如果我们的布局背景颜色是白色,可以这样解决

View decorView = getWindow().getDecorView();
         //重点:SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 
        int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 
        decorView.setSystemUiVisibility(option); 
        getWindow().setStatusBarColor(Color.TRANSPARENT);
Paste_Image.png

最后总结几点

  • 对于是图片作为背景的全屏模式,且没有标题栏的情况下。不需要设置setFitsSystemWindows这个属性。
  • 图片作为背景的全屏模式,且有标题栏的情况下。setFitsSystemWindows(false);意思是不给状态栏预留空间。
    和setClipToPadding(true);意思是可以通过padding绘制其他view
  • 非图片为背景,且有标题栏的情况下。setFitsSystemWindows(true);

以上都是基于5.0以上如何使状态栏透明效果。

4.4至5.0之间解决

其实就是通过添加一个同状态栏高度的view,再设置状态栏背景颜色解决

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
            //5.0 - 4.4之间
            //设置状态栏透明属性
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            ViewGroup decorView = (ViewGroup)activity.getWindow().getDecorView();
            int count = decorView.getChildCount();
            //生成一个状态栏大小的矩形,且添加到布局中
            if(count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView){
                //看decorView最后一个是否是StatusBarView
                decorView.getChildAt(count - 1).setBackgroundColor(color);
            } else {
                StatusBarView statusBarView = createStatusBarView(activity,color,statusBarAlpha);
                //将statusBarView添加到decorView最后;由于decorView是FrameLayout,所以statusBarView放在最顶部
                decorView.addView(statusBarView);
            }
        }

/**
     * 生成一个和状态栏大小相同的半透明矩形条
     * @param activity 需要设置的activity
     * @param color    状态栏颜色值
     * @param alpha    透明值
     * @return 状态栏矩形条
     */
private static StatusBarView createStatusBarView(Activity activity,@ColorInt int color,int alpha){
        //绘制一个和状态栏一样高的矩形 StatusBarView 本质上就是一个View而已;什么属性也没有
        StatusBarView statusBarView = new StatusBarView(activity);
        //宽是MATCH_PARENT,高度就是状态栏的高度
        LinearLayout.LayoutParams params =
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,getStatusBarHeight(activity));
        statusBarView.setLayoutParams(params);
        statusBarView.setBackgroundColor(color);
        return statusBarView;
    }

编辑状态时,状态栏问题:

当用到沉浸式状态栏时,如果界面有编辑栏的时候:
郭霖推荐的博文帮到你

我这里截取方法五的重要部分:

Paste_Image.png
Paste_Image.png
Paste_Image.png

实现步骤:

1、把SoftHideKeyBoardUtil类复制到项目中;
2、在需要使用的Activity的onCreate方法中添加:SoftHideKeyBoardUtil.assistActivity(this);即可。

/**
 * 解决键盘档住输入框
 * Created by SmileXie on 2017/4/3.
 */

public class SoftHideKeyBoardUtil {
    public static void assistActivity (Activity activity) {
        new SoftHideKeyBoardUtil(activity);
    }
    private View mChildOfContent;
    private int usableHeightPrevious;
    private FrameLayout.LayoutParams frameLayoutParams;
    //为适应华为小米等手机键盘上方出现黑条或不适配
    private int contentHeight;//获取setContentView本来view的高度
    private boolean isfirst = true;//只用获取一次
    private  int statusBarHeight;//状态栏高度
    private SoftHideKeyBoardUtil(Activity activity) {
   //1、找到Activity的最外层布局控件,它其实是一个DecorView,它所用的控件就是FrameLayout
        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
        //2、获取到setContentView放进去的View
        mChildOfContent = content.getChildAt(0);
        //3、给Activity的xml布局设置View树监听,当布局有变化,如键盘弹出或收起时,都会回调此监听  
          mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        //4、软键盘弹起会使GlobalLayout发生变化
            public void onGlobalLayout() {
            if (isfirst) {
                    contentHeight = mChildOfContent.getHeight();//兼容华为等机型
                    isfirst = false;
                }
                //5、当前布局发生变化时,对Activity的xml布局进行重绘
                possiblyResizeChildOfContent();
            }
        });
        //6、获取到Activity的xml布局的放置参数
        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
    }

    // 获取界面可用高度,如果软键盘弹起后,Activity的xml布局可用高度需要减去键盘高度  
    private void possiblyResizeChildOfContent() {
        //1、获取当前界面可用高度,键盘弹起后,当前界面可用布局会减少键盘的高度
        int usableHeightNow = computeUsableHeight();
        //2、如果当前可用高度和原始值不一样
        if (usableHeightNow != usableHeightPrevious) {
            //3、获取Activity中xml中布局在当前界面显示的高度
            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
            //4、Activity中xml布局的高度-当前可用高度
            int heightDifference = usableHeightSansKeyboard - usableHeightNow;
            //5、高度差大于屏幕1/4时,说明键盘弹出
            if (heightDifference > (usableHeightSansKeyboard/4)) {
                // 6、键盘弹出了,Activity的xml布局高度应当减去键盘高度
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
                    frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight;
                } else {
                    frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
                }
            } else {
                frameLayoutParams.height = contentHeight;
            }
            //7、 重绘Activity的xml布局
            mChildOfContent.requestLayout();
            usableHeightPrevious = usableHeightNow;
        }
    }
    private int computeUsableHeight() {
        Rect r = new Rect();
        mChildOfContent.getWindowVisibleDisplayFrame(r);
        // 全屏模式下:直接返回r.bottom,r.top其实是状态栏的高度
        return (r.bottom - r.top);
    }
}

(自己记录的属性可以忽略)
在Manifest文件的每个Activity都可设置如下图:


沉浸式状态栏StatusBar_第5张图片
Manifest下Activity标签

真正的沉浸式模式

只有在Android 4.4及以上系统才支持沉浸式模式
重写Activity的onWindowFocusChanged()方法

public class MainActivity extends AppCompatActivity {

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

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
//SYSTEM_UI_FLAG_LAYOUT_STABLE与SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN结合在一起使用,表示会让应用的主体内容占用系统状态栏的空间
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
//全屏的意思,也就是会将状态栏隐藏
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        }
    }
}

界面默认情况下是全屏的,状态栏和导航栏都不会显示。当我们需要用到状态栏或导航栏时,只需要在屏幕顶部向下拉,或者在屏幕右侧向左拉,状态栏和导航栏就会显示出来,此时界面上任何元素的显示或大小都不会受影响。过一段时间后如果没有任何操作,状态栏和导航栏又会自动隐藏起来,重新回到全屏状态。

沉浸式状态栏StatusBar_第6张图片
Paste_Image.png

隐藏导航栏

View decorView = getWindow().getDecorView();
//将状态栏和导航栏同时隐藏了
int option = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(option);
ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
                //将actionBar隐藏
                actionBar.hide();
        }

看上去好像终于是完全全屏化,但其实在这种模式下,触摸屏幕的任意位置都会退出全屏。

效果如图:

沉浸式状态栏StatusBar_第7张图片
Paste_Image.png

实现透明状态栏和导航栏

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    View decorView = getWindow().getDecorView();
//SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,表示会让应用的主体内容占用系统导航栏的空间
    int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
    decorView.setSystemUiVisibility(option);
//setNavigationBarColor()方法将导航栏设置成透明色
    getWindow().setNavigationBarColor(Color.TRANSPARENT);
    getWindow().setStatusBarColor(Color.TRANSPARENT);
}
ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
                //将actionBar隐藏
                actionBar.hide();
        }

效果:

沉浸式状态栏StatusBar_第8张图片
Paste_Image.png


作者:wyman_1007
链接:https://www.jianshu.com/p/b54e02d60c4d
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

你可能感兴趣的:(Android)