Android沈浸式狀態列攻略

來源:互聯網
上載者:User

標籤:狀態列   android   透明狀態列   沈浸式   

前言

這裡不討論[沈浸式]這個詞用得好不好, 大家聽得懂就行. 這篇文章主要是我在實際項目中的一些經驗, 整理出來和大家分享, 歡迎探討. 由於實習一直是996, 沒時間做總結, 今天突然覺得這樣的工作讓我都忘了生活了, 是時候做個了斷了. 寫這篇文章的時候已經是23:44, 來不及貼一些demo, 但是這裡的代碼都是曾經的項目中摘出來的, 是可以啟動並執行, 但我現在沒有真的執行一遍. 注意所有的代碼都只在android 4.4及以上有效.

考慮

根據實際項目的不同, 可能選擇的沈浸式實現策略也會有所不同.

傳統純色actionbar

如果你的app使用的是遵循android規範的actionbar或者有一個純色layout放在螢幕頂部, 那麼這裡有一個入侵較小的方案, 這個方案的實現方式是參考的開源項目SystemBarTint.
讓你的activity繼承一個BaseActivity, 在BaseActivity裡面重寫onPostCreate

    @Override    protected void onPostCreate(Bundle savedInstanceState) {        super.onPostCreate(savedInstanceState);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)  {        ImmerseHelper.setSystemBarTransparent(this);        }    }

重點在ImmerseHelper這個類裡, 先貼代碼再講解.

public class ImmerseHelper {    @TargetApi(Build.VERSION_CODES.KITKAT)    public static void setSystemBarTransparent(Activity paramActivity)    {        Window window = paramActivity.getWindow();        WindowManager.LayoutParams layoutParams = window.getAttributes();        layoutParams.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;        window.setAttributes(layoutParams);        hackStatusBarTransparent(paramActivity);        setContentPadding(paramActivity);    }    public static void hackStatusBarTransparent(Activity paramActivity) {        ViewGroup localViewGroup = (ViewGroup) paramActivity.getWindow().getDecorView()                .findViewById(android.R.id.content);        View colorview = new View(paramActivity);        colorview.setBackgroundResource(R.color.statusbar_color);        localViewGroup.addView(colorview, ViewGroup.LayoutParams.MATCH_PARENT,                ImmerseHelper.getStatusBarHeight(paramActivity));    }    public static void setContentPadding(Activity activity) {        ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0)            .setPadding(0, ImmerseHelper.getStatusBarHeight(activity)                     + ImmerseHelper.getActionBarHeight(activity), 0, 0);    }    /**     * 擷取狀態列高度, 單位px     * @param context     * @return     */    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;    }    /**     * 擷取actionbar高度, 單位px     * @param context     * @return     */    public static int getActionBarHeight(Context context)    {        TypedValue localTypedValue = new TypedValue();        if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, localTypedValue, true)) {            return TypedValue.complexToDimensionPixelSize(localTypedValue.data, context.getResources().getDisplayMetrics());        }        return 0;    }}

簡單來說就是在postCreate時對activity做手腳, 這樣組內其他開發人員幾乎感受不到變化, 只需要將activity的基類指定成BaseActivity就好
ImmerseHelper這個類主要做了三件事
- 將window的FLAG_TRANSLUCENT_STATUS標誌開啟
- 給某個View中添加了一個與狀態列大小完全相同的純色塊
- 給activity的根View設定paddingTop
FLAG_TRANSLUCENT_STATUS這個標誌開啟之後, 狀態列就透明了, 同時我們的activity的主體布局, 也就是setContentView傳入的那個布局, 會頂到螢幕最上方, 被狀態列蓋住一部分, 注意actionbar是不會受影響的. 所以我們在第三步給activity的根view設了paddingTop, 高度是狀態列的高度加上actionbar的高度, 這樣activity中的內容才會回到之前的正常位置. 但此時狀態列下方透明了, 所以我們給某個View中添加了一個與狀態列大小完全相同的色塊, 顏色和actionbar一致, 這樣就有了沈浸式效果.
當然這裡的效果是狀態列下面有一層半透明黑底, 之後才是我們添加的view, 所以不用擔心看不到狀態文字, 在4.4上只能做到這個效果, 5.0上可以讓狀態列底部完全透明, 這個等會兒說.
不過光有實現不行, 還需要知道為什麼我們要這麼做

原理

先看一下hierarchy view的

我們getDecorView拿到的是最左邊的DecorView, 而setContentView影響的是id/content那個view的直接子view, 和id/content平級的view是actionbar.
activity.getWindow().getDecorView().findViewById(android.R.id.content);這句拿到的是id/content這個view, 我們向其中添加一個純色view, 由於開啟了FLAG_TRANSLUCENT_STATUS, 這個純色view就直接頂在最上面, 也就是狀態列覆蓋的地方.
SystemBarTint中並非是向id/content中addView, 而是直接向getDecorView()中addView, 而SwipeBackLayout則是在DecorView和他的子View之間插入自己的layout, 相當於劫持了DecorView的子View, 所以如果同時使用這兩個開源項目不加修改, 要麼滑動返回划走的是狀態列, 要麼狀態列撕裂. 如果像我這麼寫, 就不會和SwipeBackLayout衝突.

非傳統

如果你要做到向這樣, 圖片完全頂在頂部, 那麼就不建議使用actionbar了, 同時也不需要setContentPadding這步. 有時這樣會導致某些頁面的內容過分偏上, 這個時候建議用一個dimen, 在正常情況下是0dp, v19及以上時是24dp, 這也是狀態列高度, 哪些頁面要隔開狀態列, 就用這個dimen做marginTop, 或者include一個高度為這個dimen的layout.
這樣子的入侵比較強, 做新的頁面需要時刻注意和狀態列是否需要保持距離, 很容易忘記, 不過也不算什麼困難, 畢竟一運行就看出來了.

Lolipop+特有方法

上面說的都是api level 19的方法, 唯一的缺陷是狀態列並非完全透明, 而是底部有個半透明的黑條, 在api level 21上, 我們可以去掉這個半透明黑條, 讓狀態列完全透明.
onCreate的setContentView調用之後, 將activity做參數傳給下面這個方法, 就可以讓你的app在api level 21上擁有完全透明的狀態列, 同時在api 19上使用上面的實現

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)    public void setSystemBarTransparent(Activity paramActivity)    {        if (shouldUseTransparentSystemBar()) {            Window window = paramActivity.getWindow();            WindowManager.LayoutParams layoutParams = window.getAttributes();            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {                //api 21 解決方案                View systemdecor = window.getDecorView();            systemdecor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);                layoutParams.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;                window.setStatusBarColor(0x00000000);            } else {                //api 19 解決方案                layoutParams.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;            }            window.setAttributes(layoutParams);        }    }

api 21的解決方案理論上是可以用xml完成的, 但是我實際測試發現並不能, 只有用代碼才有效.
如果你想問api 20去哪了, 可以去看看sdk manager裡面api 20的括弧裡寫的什麼.

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

Android沈浸式狀態列攻略

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.