子墨庖丁Android的ActionBar源碼分析 (一)執行個體化

來源:互聯網
上載者:User

標籤:android   actionbar   開源項目   

      如果你從事過Android用戶端開發,相信你對ActionBar這套架構並不陌生,或者說你並不瞭解它,但是你應該時不時的要跟它打交道。拋開ActionBar的實現不說,ActionBar實際上是對Android的TitleBar行為的抽象,這種架構可以適用於這種模式的應用,是對需要的行為視圖的抽象。當然或許你也和我一樣,對ActionBar的實現效率並不滿意,因為你開啟它的視圖,你會發現它的實現非常的ugly。不過我們慶幸的看到的是,ActionBar在設計的時候就並不是以一個強型別的姿態存在,我們發現它並不是以一個View的方式存在,而跟Fragment一樣是一個很單純的工具類。這種設計正好屏蔽了內部的實現,從而可以讓我們對它的實現進行改造。當然ActionBar的改造對我來說並不是文章的重點,如果你對自訂控制項已經熟門熟路了,那麼相信你閱讀完這個系列的文章以後,能更有助於你改造ActionBar。對本章我將從ActionBar產生入口開始講述。

      我們知道我們再定義一個Activity的時候,跟WMS直接掛鈎的用戶端代理是Window類,當然,我這麼說本身不準確。因為Window是間接持有這種代理類,不過這不影響我們對ActionBar的整體理解。對於Window類來說,它跟我們直接打交道是PhoneWindow。我們將調用setContentView的方式來註冊我們需要的內部視圖,為什麼說是內部視圖,因為除了我們的視圖之外,Window裡面還註冊有多個的視圖裝飾。其實這也是裝飾模式的一種,甚至很像模板方法。

@Override    public void setContentView(View view, ViewGroup.LayoutParams params) {        if (mContentParent == null) {            installDecor();        } else {            mContentParent.removeAllViews();        }        mContentParent.addView(view, params);        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }    }
我們發現實際上我們的視圖是包含在一個叫做mContentParent的ViewGroup中。而這個對象的產生是定義在Window類中的protected ViewGroup generateLayout(DecorView decor)方法中。

我們知道對於一個Window視圖的影響除了Window.LayoutParams外還有Feature。Feature對視圖的影響並不直接跟WMS打交道。即使跟WMS打交道也是通過WIndow.LayoutParams類控制,也就是說Feature本身就是一種可有可無的小甜點。在產生mContentParent的時候你會經常看到一些屬性符合代碼:

int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)                & (~getForcedWindowFlags());        if (mIsFloating) {            setLayout(Injector.getFloatingWindowWidth(getContext()), WRAP_CONTENT); // Miui Hook            setFlags(0, flagsToUpdate);        } else {            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {            // Don't allow an action bar if there is no title.            requestFeature(FEATURE_ACTION_BAR);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {            requestFeature(FEATURE_ACTION_BAR_OVERLAY);        }

我們知道我們可以制定一個視圖的主題集合,而這種主題集合中可以定製各種的外觀參數,還有特徵屬性。其中一部分要轉化成為Window.LayoutParams來跟WMS打交道。比如:

if (!hasSoftInputMode()) {            params.softInputMode = a.getInt(                    com.android.internal.R.styleable.Window_windowSoftInputMode,                    params.softInputMode);        }

我們可以看出,實際上對於一個視窗的IME管理,是需要WMS的介入,而這種介入你在用戶端設定檔中定義的時候,需要轉化成為Window.layoutparams參數的屬性,讓它來傳遞給WMS來觸發管理。

if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;        } else {            // Embedded, so no decoration is needed.            layoutResource = com.android.internal.R.layout.screen_simple;        }

我們看出,如果你已經定義了ActionBar主題項,那麼它將使用screen_simple或者overlay這兩種模式的我們只考慮simple方式,我們來看下simple的布局檔案:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:fitsSystemWindows="true">    <com.android.internal.widget.ActionBarContainer        android:id="@+id/action_bar_container"        android:layout_width="match_parent"        android:layout_height="wrap_content"        style="?android:attr/actionBarStyle">        <com.android.internal.widget.ActionBarView            android:id="@+id/action_bar"            android:layout_width="match_parent"            android:layout_height="wrap_content"            style="?android:attr/actionBarStyle" />        <com.android.internal.widget.ActionBarContextView            android:id="@+id/action_context_bar"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:visibility="gone"            style="?android:attr/actionModeStyle" />    </com.android.internal.widget.ActionBarContainer>    <FrameLayout android:id="@android:id/content"        android:layout_width="match_parent"        android:layout_height="0dip"        android:layout_weight="1"        android:foregroundGravity="fill_horizontal|top"        android:foreground="?android:attr/windowContentOverlay" />    <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"                  android:layout_width="match_parent"                  android:layout_height="wrap_content"                  style="?android:attr/actionBarSplitStyle"                  android:visibility="gone"                  android:gravity="center"/></LinearLayout>

我們可以直觀的看到這跟我們所熟知的ActionBar布局一致。縱向,ActionBar其實就是ActionBarContainer。對於ActionBar的解析和布局我們放到後面再講。我們看到當我們決定使用哪種布局之後,通過調用:

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

方法擷取mContentParent,也就是說我們的視圖就是包含在這個ActionBar容器中的Content容器中。

我們回到Activity的setConentView方法。我們在設定完我們的視圖以後,它會初始化initActionBar();

我前面已經說過了,ActionBar和Fragment本身不屬於AndroidUI系統的一部分,因此需要對它進行初始化。ActionBar的實作類別是com.android.internal.app.ActionBarImpl,由於ActionBarImpl針對的是Window,因此不論你是Activity或者是Dialog或者是PopupWindow理論上都可以使用ActionBar。這種理論實際上也可以說明一點,就是在同一個介面中出現兩個ActionBar是合理的。甚至你在同一個Window裡面不同的Fragment中實現自己的一套ActionBar也是可行的。因為它並不納入在WMS的管理中,好吧,有點扯遠了,我們繼續前文。

public ActionBarImpl(Activity activity) {        mActivity = activity;        Window window = activity.getWindow();        View decor = window.getDecorView();        init(decor);        if (!mActivity.getWindow().hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY)) {            mContentView = decor.findViewById(android.R.id.content);        }    }

我們看到ActionBarImpl的初始化主要通過init方法實現。

private void init(View decor) {        mContext = decor.getContext();        mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(                com.android.internal.R.id.action_bar_overlay_layout);        if (mOverlayLayout != null) {            mOverlayLayout.setActionBar(this);        }        mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);        mContextView = (ActionBarContextView) decor.findViewById(                com.android.internal.R.id.action_context_bar);        mContainerView = (ActionBarContainer) decor.findViewById(                com.android.internal.R.id.action_bar_container);        mTopVisibilityView = (ViewGroup)decor.findViewById(                com.android.internal.R.id.top_action_bar);        if (mTopVisibilityView == null) {            mTopVisibilityView = mContainerView;        }        mSplitView = (ActionBarContainer) decor.findViewById(                com.android.internal.R.id.split_action_bar);        if (mActionView == null || mContextView == null || mContainerView == null) {            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +                    "with a compatible window decor layout");        }        mActionView.setContextView(mContextView);        mContextDisplayMode = mActionView.isSplitActionBar() ?                CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;        // This was initially read from the action bar style        final int current = mActionView.getDisplayOptions();        final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0;        if (homeAsUp) {            mDisplayHomeAsUpSet = true;        }        ActionBarPolicy abp = ActionBarPolicy.get(mContext);        setHomeButtonEnabled(abp.enableHomeButtonByDefault() || homeAsUp);        setHasEmbeddedTabs(abp.hasEmbeddedTabs());    }

主要為了初始化一些視圖參數,還有往ActionBarOverlayLayout對象注入一個ActionBar控制回調,當然也就是它本身。但我們也能從代碼看出,ActionBar本身每個內部對象之間耦合度相對較高,互相引用,不過面向ActionBar介面來編程能有效屏蔽掉這種低效率。到這裡,實際上,對於ActionBar的執行個體化已經完成。下一章我們將開始ActionBar視圖布局的的討論。


非子墨:

QQ:1025250620

SINA:http://weibo.com/1752090185/profile?rightmod=1&wvr=5&mod=personinfo






相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.