zoukankan      html  css  js  c++  java
  • 子墨庖丁Android的ActionBar源代码分析 (一)实例化

          假设你从事过Androidclient开发,相信你对ActionBar这套框架并不陌生,或者说你并不了解它,可是你应该时不时的要跟它打交道。抛开ActionBar的实现不说,ActionBar实际上是对Android的TitleBar行为的抽象,这样的框架能够适用于这样的模式的应用,是对须要的行为视图的抽象。当然也许你也和我一样,对ActionBar的实现效率并不惬意。由于你打开它的视图,你会发现它的实现非常的ugly。

    只是我们庆幸的看到的是,ActionBar在设计的时候就并非以一个强类型的姿态存在,我们发现它并非以一个View的方式存在,而跟Fragment一样是一个非常单纯的工具类。

    这样的设计正好屏蔽了内部的实现,从而能够让我们对它的实现进行改造。当然ActionBar的改造对我来说并非文章的重点,假设你对自己定义控件已经熟门熟路了,那么相信你阅读完这个系列的文章以后,能更有助于你改造ActionBar。对本章我将从ActionBar生成入口開始讲述。

          我们知道我们再定义一个Activity的时候,跟WMS直接挂钩的client代理是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);
            }

    我们能够看出,实际上对于一个窗体的输入法管理,是须要WMS的介入,而这样的介入你在client配置文件里定义的时候,须要转化成为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






  • 相关阅读:
    线程池的工作原理
    并发处理之master-worker 模式
    Parrot os引导修复
    使用datax mongodb导数据到postgresql数据库遇到访问权限问题
    10、Flink高可用HA
    9、Standalone集群测试运行
    8、Flink Standalone工作流程
    7、Flink Local模式安装部署
    java读取word文档的文字内容
    对文件内容就行修改-java代码
  • 原文地址:https://www.cnblogs.com/llguanli/p/6753889.html
Copyright © 2011-2022 走看看