Android Activity全屏平滑切換非全屏最佳實踐,告別蜜汁卡頓

前言

項目中我們通常將啟動Activity命名為SplashActivity,并設置全屏,稍許停頓后再跳轉LoginActivity或者MainActivity等非全屏Activity(后面統稱為NormalActivity)。但是在跳到新界面的瞬間,狀態欄生硬地從內容區擠出空間,插入在屏幕頂端,造成頁面出現一次蜜汁卡頓。事故現場:

過渡時蜜汁卡頓.gif

一般實踐

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);

這樣做起到的效果是內容區會一直延伸到屏幕頂部,狀態欄蓋在內容區上面。如圖:

內容區與狀態欄重疊.jpg

然后,只需要想辦法讓內容區回到原來的高度,避免和狀態欄重疊。

拓展
如果有小伙伴跟我一樣還不熟悉setContentView()背后的布局層次,可以閱讀Android DecorView淺析》這篇博客,能幫你快速地了解。我在此借用了原博的一張圖,謝過博主。

DecorView的層次結構.png

結合拓展,想必大家已經有還原內容區的思路了。我的總結如下:

  1. 為頂部控件(ToolBarTitleBar或自定義View)設置paddingTop,值為狀態欄高度;
  2. 通過findViewById(android.R.id.content))得到編號21FrameLayout的實例,設置其marginTop,值為狀態欄高度;
  3. 手動new一個高為狀態欄高度view,利用編號1的LinearLayoutaddView()方法add到0位置上;
  4. 手動new一個高為狀態欄高度view,add到由編號21FrameLayout.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);

最終效果:

平滑過渡.gif
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容