1. 程式人生 > >Android狀態列適配原始碼解析。

Android狀態列適配原始碼解析。

前言

Android狀態列透明,狀態列著色與沉浸是我們必須要會弄的東西。 這裡告訴你各個版本怎麼適配,為什麼需要分版本來適配!

怎麼使用

狀態列透明

  • 5.0以上:
 if (Build.VERSION.SDK_INT >= 21) {
    View decorView = getWindow().getDecorView();
    int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
 // View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:
//Activity佈局全屏顯示,但狀態列不會被隱藏覆蓋, //狀態列依然可見,Activity頂端佈局部分會被狀態遮住。 //SYSTEM_UI_FLAG_LAYOUT_STABLE : //防止狀態列隱藏,保證你使用fitSystemWindows時候,系統UI邊界 //始終不會變,依然存在可見.即使你隱藏了所有,他依然存在,增加了UI穩定性, decorView.setSystemUiVisibility(option); getWindow().setStatusBarColor(Color.TRANSPARENT);//狀態列顏色設定為透明。 }
  • 4.4到5.0之間
if
(Build.VERSION.SDK_INT>=19){ WindowManager.LayoutParams myLayoutParams = getWindow().getAttributes(); myLayoutParams.flags =(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS|myLayoutParams.flags); } //這裡大家可能不懂 | 操作因為這裡的flags都是隻佔一位的1的(如000001,000010,000100,001000),其實這裡的位或操作就是將FLAG_TRANSLUCENT_STATUS的flags相加起來。詳情可看

狀態列上色

  • 5.0以上:
    官方新加入的API
 getWindow().setStatusBarColor(int Color);
 //想要什麼顏色就用什麼顏色,一步到位
  • 4.4到5.0之間
    這裡由於官方沒有給出設定狀態列的顏色API,所以我們需要使用先透明狀態列,然後fitSystemWindows==true,然後填充一個等高的純色View,來模擬狀態列已經上色了。(fitSystemWindows為true時候,系統UI會佔據填充屬於他自己的螢幕高度,false時候為使得整個Activity佔據螢幕)

  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
        {      
            ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);

            View statusBarView = new View(activity);
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    getStatusBarHeight(activity));//獲得高和等寬的佈局屬性
            statusBarView.setBackgroundColor(int color);//設定需要的顏色作為背景填充
            contentView.addView(statusBarView, lp);
        }

    }
 public static int getStatusBarHeight(Context context)
    {
        int result = 0;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0)
        {
            result = context.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }//這裡參考hongyang的程式碼
  • 狀態列顏色與頭佈局顏色相同適配(類似QQ)
    設定狀態列為透明,讓頭佈局侵入狀態列高度,fitSystemWindows==false預設也為false,設定第一個佈局的paddingtop為狀態列高度即可實現這個效果。比如toolbar或者一些自定義的title,(padding為元素邊框與元素內容之間的空間距離)
    這裡寫圖片描述
    可以看到這裡我們設定Textview為頭佈局,已經侵入狀態列,如果我們設定一個panddingtop就不是OK了!

參考

如果看到這一步:相信我們能夠完全適配各種需求對於我們的狀態列。但是為什麼要分這麼多版本來適配呢?這裡我分原始碼 !!15,19,22!!,3個版本來跟進原始碼檢視結果如下

  • 各種版本都可以得到getwindow.getdecorview() 得到decorview,(decorview
    為最底層介面) 觀察這裡View.java的最低版本15也支援setsystemUIvisiblity()。但是為什麼4.4才能支援透明呢?跟進

4.4以上

   /** @param visibility  Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
     * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN},
     * {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
     * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE},
     * and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}.
     */
    public void setSystemUiVisibility(int visibility) {
        if (visibility != mSystemUiVisibility) {
            mSystemUiVisibility = visibility;
            if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
                mParent.recomputeViewAttributes(this);
            }
        }
    }

4.4以下

 /**
     * @param visibility  Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE} or
     * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}.
     */
    public void setSystemUiVisibility(int visibility) {
        if (visibility != mSystemUiVisibility) {
            mSystemUiVisibility = visibility;
            if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
                mParent.recomputeViewAttributes(this);
            }
        }
    }

從這裡就可知,這一些SystemUI_flag是4.4之後Android官方才做的適配flags;之前並沒有這麼多設定沉浸透明相關的系統flag。

  • getwinodw().setStatuBarColor()
    檢視3個版本的window實現的實現類phonewindow內部可得只有API大於21才有明確的API能夠設定狀態列顏色。低於5.0不能直接通過API設定顏色。
@Override
    public void setStatusBarColor(int color) {
        mStatusBarColor = color;
        mForcedStatusBarColor = true; //用於判斷是否需要上色狀態列。
        if (mDecor != null) {          //mDecor也就是Decorview物件。
            mDecor.updateColorViews(null, false /* animate */);
        }
    }

跟進方法
private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
            WindowManager.LayoutParams attrs = getAttributes();
        //getAttributes()跟進發現就是 WindowManager.LayoutParams();

            int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();

                 //首先得到我們佈局屬性與系統預設的uivisibility,什麼不設定的話起初為getWindowSystemUiVisibility()為0。
             //  這裡就算不是0其實也無所謂。因為systemUiVisibility這一類引數都是隻佔一位的比如  000001。000010。000100。....進行或操作只能是全部增加,!這裡其實也就是回到我們4.4版本設定UI透明可以用來設定引數的條件。         
         //。。。。。。。                                           
        //中間一段程式碼省略,是判斷高低端裝置對是否使用硬體影象加速處理什麼的,
        //。。。。。。。。
updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
                        mLastTopInset, animate && !disallowAnimate); 
                     //最後傳入我們得到的sysUiVisibility 來更改狀態列顏色
                     //此方法從方法名可知道就是修改顏色的不深究.內部作一些判斷顏色是否透明,是否需要隱藏判斷,以及具體的顏色處理。
                 //

            //這一個完整的過程也就完成了我們的狀態列修改完成了。
            }

最後貼出幾個引數的意義

View.SYSTEM_UI_FLAG_VISIBLE :狀態列和Activity共存,Activity不全屏顯示。也就是應用平常的顯示畫面
View.SYSTEM_UI_FLAG_FULLSCREEN :Activity全屏顯示,且狀態列被覆蓋掉
.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN :Activity全屏顯示,但是狀態列不會被覆蓋掉,而是正常顯示,只是Activity頂端布 局會被覆蓋住
View.SYSTEM_UI_FLAG_LAYOUT_STABLE :
防止狀態列隱藏,保證你使用fitSystemWindows時候,系統UI邊界
始終不會變,依然存在可見.即使你隱藏了所有,他依然存在,增加了UI穩定性,迫使確認使用者瞭解如何讓UI控制元件回來。
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY:向內滑動的操作會讓系統欄臨時顯示,並處於半透明的狀態。此時沒有標籤會被清除,系統UI可見性監聽器也不會被觸發。如果使用者沒有進行操作,系統欄會在一段時間內自動隱藏。