Android沉浸式状态栏小结
来源:互联网 发布:ipp6.0软件下载 编辑:程序博客网 时间:2024/05/18 03:41
一、设置状态栏的颜色
Android 4.4系统及其以上的系统才能生效。
<resources> <!-- Base application theme. --> <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> </style></resources>关于每个属性的意思看下图就可以知道了。
二、设置状态栏为透明状态
windowTranslucentStatus可以设置状态栏为透明状态,但是它只能使用在Android 4.4(API 19)及其以上的系统上。
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowTranslucentStatus">true</item> </style></resources>需要说明的是,使用上面属性,Android 4.4 和 Android 5.0及其以后的系统是有区别的,Android 4.4系统效果为透明状态,5.0及其以后系统效果为半透明状态。
Android 4.4系统效果为透明状态
5.0及其以后系统效果为半透明状态
我们一般是设置Theme为Theme.AppCompat.Light.NoActionBar,然后自己定义一个ToolBar,当我们设置windowTranslucentStatus为true的时候,最终得到的效果如下:
可以看到,布局是从状态栏开始的,ToolBar移到了状态栏中,处理方法有:
1、得到状态栏的高度,然后设置ToolBar的Padding为状态栏的高度。
// A method to find height of the status barpublic int getStatusBarHeight() { int result = 0; int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { result = getResources().getDimensionPixelSize(resourceId); } return result;}
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_drawer); // Retrieve the AppCompact Toolbar Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Set the padding to match the Status Bar height toolbar.setPadding(0, getStatusBarHeight(), 0, 0);}
效果如下:
2、给ToolBar添加android:fitsSystemWindows="true"
<android.support.v7.widget.Toolbar android:id="@+id/id_toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" android:fitsSystemWindows="true" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>android:fitsSystemWindows这个属性,主要是通过调整当前设置这个属性的view的padding去为我们的status_bar留下空间。
FitSystemWindow的原理
Android4.4与Android5.0的Insets处理机制完全不同。
Android 5.0的机制:
private void performTraversals() { …… dispatchApplyInsets(host); …… performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); …… performLayout(lp, desiredWindowWidth, desiredWindowHeight); …… performDraw();} void dispatchApplyInsets(View host) { host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */));}
SystemBar的尺寸在WindowInsets 中表示出来,比如Insets:(0, 63 , 0, 126)。表示StatusBar高度63,NavigationBar高度126.
dispatchApplyWindowInsets 将WindowInsets 从View树顶部开始分发。
首先我们来看看ViewGroup.java
@Overridepublic WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { // 1、执行父类的dispatchApplyWindowInsets方法 // 其实就是执行View的dispatchApplyWindowInsets方法 // 目的就是看是否需要自己消费掉Insets insets = super.dispatchApplyWindowInsets(insets); // 2、如果自己不处理,那么就处理子View给它的子View处理 if (!insets.isConsumed()) { final int count = getChildCount(); for (int i = 0; i < count; i++) { insets = getChildAt(i).dispatchApplyWindowInsets(insets); if (insets.isConsumed()) { break; } } } return insets;}
再来看看View的dispatchApplyWindowInsets方法
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) { return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets); } else { return onApplyWindowInsets(insets); }}
可以看到分为两步
1、如果设置View监听
View接收到Insets。会先判断自己是否被注册了监听,监听是指这个,在这个监听里能够收到Insets。并依据自己情况处理。
设置监听的方法
public void setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener) { getListenerInfo().mOnApplyWindowInsetsListener = listener;}也就是说,我们可以设置监听自己来处理
2、执行onApplyWindowInsets方法
public WindowInsets onApplyWindowInsets(WindowInsets insets) { if (fitSystemWindowsInt(insets.getSystemWindowInsets())) { //如果fitSystemWindowsInt返回true就消耗Instes,好简单的逻辑 return insets.consumeSystemWindowInsets(); } return insets;}
重点来了fitSystemWindowsInt.它实质性的判断并设置了Padding。
private boolean fitSystemWindowsInt(Rect insets) { //如果设置了FITS_SYSTEM_WINDOWS这个flag if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) { mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; Rect localInsets = sThreadLocal.get(); if (localInsets == null) { localInsets = new Rect(); sThreadLocal.set(localInsets); } //computeFitSystemWindows主要就是localInsets=insets。并清空insets boolean res = computeFitSystemWindows(insets, localInsets); mUserPaddingLeftInitial = localInsets.left; mUserPaddingRightInitial = localInsets.right; //直接应用这个Insets到padding internalSetPadding(localInsets.left, localInsets.top, localInsets.right, localInsets.bottom); return res; } return false;}
而FITS_SYSTEM_WINDOWS这个flag有2个来源:
1、代码手动设置:
public void setFitsSystemWindows(boolean fitSystemWindows) { setFlags(fitSystemWindows ? FITS_SYSTEM_WINDOWS : 0, FITS_SYSTEM_WINDOWS);}
2、在XML中设置android:fitSystemWindow="true":
case com.android.internal.R.styleable.View_fitsSystemWindows: if (a.getBoolean(attr, false)) { viewFlagValues |= FITS_SYSTEM_WINDOWS; viewFlagMasks |= FITS_SYSTEM_WINDOWS; } break;
设置了fitSystemWindow,默认就会消费掉Insets,并设置padding。
这里我们就可以知道,其实对某个View设置了fitSystemWindow,本质就是为它设置了一个padding。
Android 4.4的机制:
在4.4中,直接设置padding,逻辑没有5.0那么复杂
private void performTraversals() { …… host.fitSystemWindows(mFitSystemWindowsInsets); …… performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); …… performLayout(lp, desiredWindowWidth, desiredWindowHeight); …… performDraw();}
让DecorView执行fitSystemWindows
首先看看ViewGroup.java
@Overrideprotected boolean fitSystemWindows(Rect insets) { // 1、执行父类的fitSystemWindows // 实质就是执行View的fitSystemWindows,看自己是否需要处理掉insets boolean done = super.fitSystemWindows(insets); // 2、如果自己不处理,就遍历它的孩子给子View处理 if (!done) { final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < count; i++) { done = children[i].fitSystemWindows(insets); if (done) { break; } } } return done;}
下面看看View.java
protected boolean fitSystemWindows(Rect insets) { if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) { mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; Rect localInsets = sThreadLocal.get(); if (localInsets == null) { localInsets = new Rect(); sThreadLocal.set(localInsets); } boolean res = computeFitSystemWindows(insets, localInsets); mUserPaddingLeftInitial = localInsets.left; mUserPaddingRightInitial = localInsets.right; internalSetPadding(localInsets.left, localInsets.top, localInsets.right, localInsets.bottom); return res; } return false;}
可以看到,跟5.0一样,看是否设置了fitSystemWindow属性,如果设置了,就设置一个padding。
3、使用开源库SystemBarTint
它可以设置状态栏的颜色和透明度
// create our manager instance after the content view is setSystemBarTintManager tintManager = new SystemBarTintManager(this);// enable status bar tinttintManager.setStatusBarTintEnabled(true);// enable navigation bar tinttintManager.setNavigationBarTintEnabled(true);// set the transparent color of the status bar, 20% darkertintManager.setTintColor(Color.parseColor("#20000000"));
基于以上,我们一般会定义两套样式
1、在values文件夹中的styles.xml
<resources> <style name="BaseAppTheme" 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> </style> <!-- Base application theme. --> <style name="AppTheme" parent="@style/BaseAppTheme"> </style></resources>在Android 4.4以下系统中,它没有任何效果。
2、在values-v19文件夹中的样式styles.xml
<resources> <style name="AppTheme" parent="@style/BaseAppTheme"> <item name="android:windowTranslucentStatus">true</item> </style></resources>
因为windowTranslucentStatus只能用在Android 4.4 及其以上的系统中。
补充:
StatusBar:
NavigationBar:
android从4.4开始,开始支持UI使用StatusBar与NavigationBar的范围。
所以要进行下面的配置:
在value中的styles.xml中设置
<!-- Base application theme. --><style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --></style><style name="AppTheme" parent="AppTheme.Base"></style>
在value-v19中的styles.xml中设置(为了兼容4.4)
<style name="AppTheme" parent="AppTheme.Base"> <item name="android:windowTranslucentStatus">true</item> <item name="android:windowTranslucentNavigation">true</item></style>
在value-v21中的styles.xml中设置
<style name="AppTheme" parent="AppTheme.Base"> <!--透明状态栏--> <item name="android:windowTranslucentStatus">true</item> <!--透明导航栏--> <item name="android:windowTranslucentNavigation">true</item> <!--使状态栏,导航栏可绘制--> <item name="android:windowDrawsSystemBarBackgrounds">true</item></style>
参考文章:Android and the transparent status bar
Android 沉浸式 UI 实现及原理
Android App 沉浸式状态栏解决方案
- Android沉浸式状态栏小结
- Android 沉浸式状态栏
- android 沉浸式 状态栏
- android 沉浸式状态栏
- android沉浸式状态栏
- Android沉浸式状态栏
- Android 沉浸式状态栏
- android 沉浸式状态栏
- Android沉浸式状态栏
- Android沉浸式状态栏
- Android 沉浸式状态栏
- Android沉浸式状态栏
- android 沉浸式状态栏
- android 沉浸式状态栏
- Android沉浸式状态栏
- android沉浸式状态栏
- Android 沉浸式状态栏
- android沉浸式状态栏
- 1.for(;;) 与 while(true) 的 区别
- 西电网络赛 - A
- MySQL数据约束
- 【leetcode】String——Roman to Integer(13)
- MarkDown的使用
- Android沉浸式状态栏小结
- Java数组操作的方法
- 问题1-网上商城
- Android位置服务--BaiduMap的使用(2)
- linux命令
- 西电网络赛 - B
- Pascal's Triangle II --杨辉三角形-链表和数组
- 第四周项目四(2)-程序分析
- 西电网络赛 - C