實現Android K的假裝沈浸式,android假裝沉浸
在Android 5.0之後引入了MD風格,並且狀態列沉浸也成為了一種設計習慣。而停留在之Android L之前的Android系統則不能直接實現沈浸式,這裡就介紹一下如何?Android K系列的假裝沈浸式。
關於沈浸式效果,這裡隨便貼幾張圖吧
Android L
Android 4.2
可以看出在Android K系列中,狀態列是漸層的效果
下面開始講解如何?的效果,在此就不贅述Android L或以上的沈浸式的效果實現了,在xml上配置幾個屬性就可以了
題外話:這篇文章介紹的都是使用Android Studio作為IDE開發Android的,如果使用的是其他IDE應按照相關設定進行設定或者配置xml等檔案。
開始學習假裝沈浸式
所謂的其他知識,就是建立一個其他App中的Activity共有的基類BaseActivity,讓其繼承AppCompatActivity,然後在基類中實現假裝沈浸式,這樣Acticity就能專註於自己的內容,而把這些共有的設定交給BaseActivity處理。
現在看看這部分如何處理,先看代碼:
1 public class BaseActivity extends AppCompatActivity { 2 3 private int mColor; 4 5 public BaseActivity(){ 6 } 7 8 public BaseActivity(int color){ 9 super();10 mColor = color;11 }12 13 @Override14 protected void onCreate(@Nullable Bundle savedInstanceState) {15 super.onCreate(savedInstanceState );16 StatusBarCompat.compat(this, ContextCompat.getColor(this, mColor));17 }18 19 public static class StatusBarCompat{20 21 private static final int INVALID_VAL = -1;22 private static final int COLOR_DEFAULT = Color.parseColor("#20000000");23 24 public static void compat(Activity activity, int statusColor){25 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){26 if(statusColor != INVALID_VAL){27 activity.getWindow().setStatusBarColor(statusColor);28 }29 return;30 }31 32 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT33 && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){34 int color = COLOR_DEFAULT;35 ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);36 37 if(statusColor != INVALID_VAL){38 color = statusColor;39 }40 41 View statusBarView = new View(activity);42 ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,43 getStatusBarHeight(activity));44 statusBarView.setBackgroundColor(color);45 46 contentView.addView(statusBarView, lp);47 //contentView.addView(statusBarView, 0, lp);48 }49 }50 51 public static void compat(Activity activity){52 compat(activity, INVALID_VAL);53 }54 55 public static int getStatusBarHeight(Context context){56 int result = 0;57 58 int resourceId = context.getResources().getIdentifier("status_bar_height",59 "dimen", "android");60 61 if(resourceId > 0){62 result = context.getResources().getDimensionPixelSize(resourceId);63 }64 65 return result;66 }67 }68 }
跟著步驟,沒必要全部看那些淩亂的代碼,在這裡講解重點部分,在BaseActivity中提供了一個單一參數的建構函式,目的是讓子類選擇假裝沉浸的顏色,在onCreate函數中調用了StatusBarCompat.compat(this, ContextCompat.getColor(this, mColor))函數,StatusBarCompat是其內部類,我們看看compat函數是如何?假裝沉浸的。
第一個if是判斷系統如果為Android L或以上的系統則直接調用setStatusBarColor函數,這是API 21之後提供設定狀態列顏色的函數,然後是第二個if用於判斷是否為Android K系統,如果是則開始我們的步驟。裡面的內容其實是這樣的,我們在XML裡配置好狀態列的背景為透明,然後在我們的布局中插入一個和狀態列等高的View,從而在視覺上看起來就是沈浸式的效果了。從字面上很容易理解這樣做的意義,然後簡單分析一下可行性。首先,如何擷取我們的布局,方法很多,可以給根布局設定一個id,然後Activity.findViewById直接擷取根布局。其實在這裡也可以不擷取布局,可以在xml中設定一個View,然後在這裡擷取這個View並且設定它的高度為狀態列的高度,背景為Activity中傳過來的顏色即可。這裡就不介紹這兩種方法,介紹一種擷取我們根布局的其他方法。代碼35行findViewById(android.R.id.content)擷取了一個FrameLayout,然後奇怪了,為什麼是擷取一個FrameLayout呢,其實是這樣的,我們的布局都是設定在AppCompatActivity的FrameLayout布局中的,這段代碼就是為了擷取這個ViewGroup的內容的,因此,現在我們手中就掌握了整個Activity的ViewGroup,我們的操作就在這裡進行。擷取了真正的根布局,當然是開始建立一個View,然後讓其高度和狀態列高度一致,然後再設定到剛剛擷取的ViewGroup中去就可以了。其中getStatusBarHeight就是擷取系統狀態列高度的函數,然後設定View的背景色,然後add到ViewGroup就完成了整個操作。現在工作還沒完成呢,既然是Android K的機子,先配置XML吧,在res目錄下建立一個values-v19的檔案夾,然後建立styles.xml檔案,貼上以下代碼:
1 <?xml version="1.0" encoding="utf-8"?>2 <resources>3 <style name="AppTheme" parent="@style/BaseAppTheme">4 <item name="android:windowTranslucentStatus">true</item>5 </style>6 </resources>
順便看看BaseAppTheme的代碼,這部分代碼在res/values/styles裡面:
1 <style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">2 <!-- Customize your theme here. -->3 <item name="colorPrimary">@color/colorPrimary</item>4 <item name="colorPrimaryDark">@color/colorPrimaryDark</item>5 <item name="colorAccent">@color/colorAccent</item>6 </style>
其中android:windowTranslucentStatus屬性設定的是狀態列是否設定為透明狀態,這個是v19之後才引入的屬性。
到這裡,看似好像完成了,但問題還沒完全解決,那就是ToolBar的空間位置問題,假如不處理,
看出效果是ToolBar和狀態列擠成了一塊,但這不是這篇文章中所想要的效果,那應該怎麼辦呢?其實只需設定ToolBar的一個屬性android:fitsSystemWindows="true"即可。該屬性的意義就是讓當前設定這個屬性的view設定對應的padding為狀態列騰出對應的空間,說白了就好像是paddingTop=statusHeight。
按步驟設定好之後,還有一步,不然看到的是代碼裡面預設設定的顏色,所以要在Activity中傳遞color給BaseActivity,簡單的代碼如下:
1 public MainActivity(){2 this(R.color.colorPrimary);3 }4 5 private MainActivity(int color){6 super(color);7 }
只需修改R.color.colorPrimary為自己需要的顏色即可。為什麼需要這樣操作,很簡單,因為預設啟動調用的是無參建構函式,所以讓無參去調用有參構造,即可實現,這也解釋了為什麼父類要去定義這個有參構造了。
運行了一下效果,嗯,還不錯,但當你如果用的是這個方法添加的view作狀態列背景色的話,其實有一個坑,不過我貼的代碼上已經解釋了這個坑的處理方法了。這個坑其實就是和DrawerLayout配合使用的時候,當你開啟抽屜的時候,會看到側滑菜單在狀態列的下面,:
可以看到,狀態列把側滑菜單擋住了,說白了其實是我們添加的view把狀態列擋住了。如何解決呢,在上面代碼中注釋了一句,其實使用下面那個三參數的函數就可以解決這個問題了,
可以看出,側滑菜單已經覆蓋在假裝狀態列的那個view上面了。為什麼會這樣呢,理由很簡單,使用第一個函數的時候它是直接add到FrameLayout最上面的,所以就擋在了我們的布局上面,而addView(statusBar, 0, lp)的意思則是插入到第1個View的位置,因此就會讓它插入到我們的布局下面,因此就實現了我們所想要的效果。既然兩個參數有先後問題,那我們是否可以在Activity的onCreat函數中這樣操作呢,比如先super.onCreat然後再調用setContentView,看似這樣子我們的布局就應該在假裝狀態列背景的view上面了,其實這是一個坑,我們在onCreat中必須是先調用setContentView然後再調用基類的onCreat,否則,狀態列的效果
其實,從setContentView的名字可以看出來為什麼了,既然這個是設定自訂布局,顯然,調用這個函數的時候會將FrameLayout裡面的內容清空,因此後調用這個函數的時候,之前add
進去的view已經被清空了。
到這裡,關於Android K的假裝沈浸式效果就實現了。