前言
項目中我們通常將啟動Activity命名為SplashActivity
,并設置全屏,稍許停頓后再跳轉LoginActivity
或者MainActivity
等非全屏Activity(后面統稱為NormalActivity
)。但是在跳到新界面的瞬間,狀態欄生硬地從內容區擠出空間,插入在屏幕頂端,造成頁面出現一次蜜汁卡頓。事故現場:
一般實踐
Google后可以找到一些解決方案,其中主要圍繞這篇文章《Switching-from-Full-Screen-to-Non-Full-Screen-Smoothly-in-Android》的思想。總結一下,首先在onCreate()
中調用setContentView()
之前插入兩行代碼:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
這樣做起到的效果是內容區會一直延伸到屏幕頂部,狀態欄蓋在內容區上面。如圖:
然后,只需要想辦法讓內容區回到原來的高度,避免和狀態欄重疊。
拓展:
如果有小伙伴跟我一樣還不熟悉setContentView()
背后的布局層次,可以閱讀《Android DecorView淺析》這篇博客,能幫你快速地了解。我在此借用了原博的一張圖,謝過博主。
DecorView的層次結構.png
結合拓展,想必大家已經有還原內容區的思路了。我的總結如下:
- 為頂部控件(
ToolBar
、TitleBar
或自定義View
)設置paddingTop
,值為狀態欄高度; - 通過
findViewById(android.R.id.content))
得到編號21FrameLayout
的實例,設置其marginTop
,值為狀態欄高度; - 手動new一個高為狀態欄高度view,利用編號1的
LinearLayout
的addView()
方法add到0位置上; - 手動new一個高為狀態欄高度view,add到由編號21
FrameLayout.addView()
,然后為我們的contentView設置MarginTop
,高度為狀態欄高度。
獲取狀態欄高度的方法:
int resId = getResources().getIdentifier("status_bar_height", "dimen", "android");
int statusBarHeight = getResources().getDimensionPixelSize(resId);
// getResources()也可以替換為Resources.getSystem(),這樣的好處是不受Context的限制,從而可在任意地方調用
經試驗以上方法均能夠解決Activity跳轉卡頓的問題。然鵝,主要在適配方面又會造成新的困擾(開發5分鐘,適配兩小時 (? _ ?))。
- 如果你的應用采用了沉浸狀態欄設計,但狀態欄顏色和
ToolBar
顏色不完全一致(比如微信),使用方法1會造成整體設計風格被破壞。 - 方法2較方法1看上去差不多,然而,一些國內的ROM(如Flyme)系統可以自動幫你的App實現沉浸狀態欄(即使你什么都沒做)可謂非常先進和人性化。但是設置margin后有可能看到的狀態欄背景和狀態圖標顏色幾乎相近,既分辨不出圖標又相當難看。
- 方法3、4的方式更為靈活,就是稍微麻煩一點。
最佳實踐
我們費盡心思在NormalActivity
上做文章,結果到頭來發現還會遇到適配這個坑,累覺不愛啊!前面扯了那么多,終歸一句話:太!麻!煩!了!
所以重點來了。在SplashActivity
中有沒有文章可做呢?嘿嘿,當然是有的,而且思想非常簡單:
設置SplashActivity
為全屏,跳轉其他Activity前退出全屏模式。
第一步,設置SplashActivity
全屏
方法1:AndroidManifest注冊全屏(AppCompat)
AndroidManifest.xml
<activity
android:name=".SplashActivity"
android:theme="@style/AppFullScreenTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
style.xml
<style name="AppFullScreenTheme" parent="@style/Theme.AppCompat.Light">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
方法2:在Activity的onCreate()
中,調用setContentView()
之前調用
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
第二步,在finish()
之前調用
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
最終效果: