Android中View的相关知识(5)

来源:互联网 发布:xy苹果助手mac版 编辑:程序博客网 时间:2024/04/30 01:56

Android中View的相关知识(5)

@(Android)

在上一篇的文章Android中View的相关知识(4)中,我们讲解了setContentView方法中getWindow().setContentView();方法,即创建content视图的过程,在本章节,我们继续往下走,探索initWindowDecorActionBar();看看ActionBar是如何创建的~:

接着分析initWindowDecorActionBar();这一路~~

老规矩,从Activity开始,Activity的源码中提供了3个重载的setContentView的方法:

    public void setContentView(int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();    }    public void setContentView(View view) {        getWindow().setContentView(view);        initWindowDecorActionBar();    }    public void setContentView(View view, ViewGroup.LayoutParams params) {        getWindow().setContentView(view, params);        initWindowDecorActionBar();    }

从这些setContentView();方法中,每个方法都有initWindowDecorActionBar();但是当当前的Activity是另一个Activity的子Activity时,或者该Activity不含属性值Window.FEATURE_ACTION_BAR,或者当前Activity已经有一个ActionBar时,initWindowDecorActionBar();方法不进行任何操作,否则初始化ActionBar,并对其设置相应的属性。

我们回到Activity类的setContentView();方法中,既然上面跟content有关,那么这个initWindowDecorActionBar();方法肯定就是和Title有关的了。附上手机屏幕层次图
Alt text

我们进入此方法中:

//Creates a new ActionBar, locates the inflated ActionBarView, initializes the ActionBar with the view, and sets mActionBarprivate void initWindowDecorActionBar() {        Window window = getWindow();        // Initializing the window decor can change window feature flags.        // Make sure that we have the correct set before performing the test below.        window.getDecorView();        if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {            return;        }        mActionBar = new WindowDecorActionBar(this);     mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);        mWindow.setDefaultIcon(mActivityInfo.getIconResource());        mWindow.setDefaultLogo(mActivityInfo.getLogoResource());

我们把源码中的注释加上,可以看到,此方法的作用就是创建一个ActionBar。
首先通过getWindow();方法获得一个Window对象,通过widnow.getDecorView();方法获得mDecor对象,(当然具体实现肯定是PhoneWindow)这个过程很熟悉,就是我们上面分析的installDecor()的过程,这里不再讲解,然后进行判断(1.当前的Activity是否嵌套在另一个Activity中;2.当前Window是否设置为有ActionBar;3.ActionBar是否为空)在这3个判断只要其中一个为真就直接返回,不创建ActionBar了;如果上述条件不成立,再往下走,就是创建ActionBar的过程以及它的各种属性的设置了。
首先new一个WindowDecorActionBar();将其赋值给mActionBar; 这个WindowDercorActionBar就是ActionBar的子类。可见具体的实现就是由这个WindowDecorActionBar来操作了。

public class WindowDecorActionBar extends ActionBar implements          ActionBarOverlayLayout.ActionBarVisibilityCallback {        ...        //省略了代码。        ...        public WindowDecorActionBar(Activity activity) {         mActivity = activity;         Window window = activity.getWindow();         View decor = window.getDecorView();         boolean overlayMode = mActivity.getWindow().hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY);         init(decor);         if (!overlayMode) {             mContentView = decor.findViewById(android.R.id.content);         }     }     ...}

从上面的代码我们一步步分析,首先,传入Activity的参数,调用activity.getWindow();方法获取到窗口对象Window,然后调用widnow.getDecorView();方法获取到decor对象,接着就是进行init(); 这里有个boolean判断,这是设置activity的窗口属性的。 FEATURE_ACTION_BAR_OVERLAY是指覆盖在内容之上的ActionBar。我们继续深入进入init();方法

private void init(View decor) {         mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(                 com.android.internal.R.id.decor_content_parent);         if (mOverlayLayout != null) {             mOverlayLayout.setActionBarVisibilityCallback(this);         }         mDecorToolbar = getDecorToolbar(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);         mSplitView = (ActionBarContainer) decor.findViewById(                 com.android.internal.R.id.split_action_bar);         if (mDecorToolbar == null || mContextView == null || mContainerView == null) {             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +                     "with a compatible window decor layout");         }         mContext = mDecorToolbar.getContext();         mContextDisplayMode = mDecorToolbar.isSplit() ?                 CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;         // This was initially read from the action bar style         final int current = mDecorToolbar.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());         final TypedArray a = mContext.obtainStyledAttributes(null,                 com.android.internal.R.styleable.ActionBar,                 com.android.internal.R.attr.actionBarStyle, 0);         if (a.getBoolean(R.styleable.ActionBar_hideOnContentScroll, false)) {             setHideOnContentScrollEnabled(true);         }         final int elevation = a.getDimensionPixelSize(R.styleable.ActionBar_elevation, 0);         if (elevation != 0) {             setElevation(elevation);         }         a.recycle();     }

首先通过findViewById的方式,获取到decor的布局,并且赋值给ActionBarOverlayLayout其实,在实例化DecorView的方法generateDecor();中,就能看出,window根据不同的features加载不同的布局文件,)我们来画个图看下DecorView的结构~
Alt text

从这个结构图和上面的代码可以看出ActionBarSplitActionBar本质上为ActionBarContainer,里面放置的就是ActionBarView或者ActionBarContextView;
先来看这个容器:ActionBarContainer

/* *This class acts as a container for the action bar view and action mode context views. It applies special styles as needed to help handle animated transitions between them. */public class ActionBarContainer extends FrameLayout {    ...    //省略了其中的代码    ...}

可以看出ActionBarContainer本质上是个FrameLayout,(这一点跟DecorView有点类似),对这个类的注释很清楚,我把这个注释也粘了过来,注释说的是这个ActionBarContainer充当的是ActionBarViewActionBarContextView的容器,而且这个容器在处理特别的ActionBar风格的时候,也要实现动画切换。好了,这个容器不用多讲,我们继续往下走,看看ActionBarViewActionBarContextView:

public class ActionBarView extends AbsActionBarView implements DecorToolbar {    ...    public void initProgress() {    ...    }     }     public void initIndeterminateProgress() {         ...     }     ...     public void setSplitToolbar(boolean splitActionBar) {        ...      }     ...    public void setEmbeddedTabView(ScrollingTabContainerView tabs) {        ...     }     public void setMenu(Menu menu, MenuPresenter.Callback cb) {        ...    }    ...    public void setCustomView(View view) {        ...    }    ...    public void setDisplayOptions(int options) {        ...    }    ...    public void setNavigationMode(int mode) {        ...    }    ...    protected void onFinishInflate() {        ...    }    ...    private void initTitle() {        ...    }    ...    public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {        ...    }    ...}

从代码中可以看出,ActionBarView的布局都是通过addView()的方式进行创建的,我们就看看ActionBarView类中到底哪些方法调用了addVIew();我把使用过addView();的方法全都列了出来。咱们进行分析:

  1. inProgress();initIndeterminateProgress();这两个方法都是初始化进度条的方法。前一个是水平进度条,后一个是圆形进度条。两个方法相差不大,首先,new 一个ProgressBar赋值给相应的申明的ProgressView;其次设置ProgressView的各种属性,最后addView将此ProgressView加入到ActionBar.

  2. setSplitToolbar();判断此ActionBar是否需要split,里面是初始化SpliteActionBar的方法;里面也有各种的判断,需要就addView将其加入;

  3. setEmbeddedTabView();它负责的把Tab容器添加到ActionBarView中,当NavigationMode=NAVIGATION_MODE_TABS(页签)时,这个方法才有效;传入的参数tabs是一个ScrollingTabContainerView类型的对象,ScrollingTabContainerView继承于HorizontalScrollView,可以看出它是一个具备水平滚动条的tab容器;当ActionBarPolicy的方法hasEmbeddedTabs返回true时,tab就会embedActionBarView上,否则就会放到ActionBarContainer上,借用网上的一张图表示下这个方法的调用过程:
    Alt text

  4. setMenu();这个是设置ActionBar上菜单的方法,在这个方法中,系统会把menuView放到ActionBar中;

  5. setCustomView();这个方法是把自定义View放置到ActionBarView上;调用的过程借用网上的一张图来展示:
    Alt text

  6. setDisplayOptions();这个是设置ActionBar显示选项的方法。根据不同的options;设置不同的ActionBar显示;

  7. setNavigationMode();这是设置ActionBar导航模式的方法,对于mode的值,有三个值可选:NAVIGATION_MODE_STANDARD(标准)、NAVIGATION_MODE_LIST(列表)、NAVIGATION_MODE_TABS(页签),当mode=NAVIGATION_MODE_LIST时,就会把spinner控件放置到ActionBarView上;当mode=NAVIGATION_MODE_TABS时,就会把TabScrollView控件放置到ActionBarView上;
  8. onFinishInflate();这是对布局进行inflate后回调的方法,在这个方法中,会把mHomeLayout先添加到mUpGoerFive,然后再把mUpGoerFive放置到ActionBarView中,这个mUpGoerFive是一个ViewGroup,它里面包含两个视图:mHomeLayoutmTitleLayout,其中mHomeLayout包含两个ImageView:mUpView(即返回的指示图标)和mIconView(默认情况下是应用程序图标),这两个View分别可以通过getActionBar().setDisplayHomeAsUpEnabled()getActionBar().setDisplayShowHomeEnabled()来设置是否显示;而mTitleLayout是一个LinearLayout;
  9. initTitle();这个是初始化ActionBarTitle的方法。
  10. expandItemActionView();这个是判断是否有扩展的菜单项的方法;
    好了,ActonBarView的这些主要的添加View的方法已经看完了。我们接着往下走,看看ActionBarContextView,其实光看名字,就知道它跟ActionBarView差不多;
public class ActionBarContextView extends AbsActionBarView implements AnimatorListener {    ...     public void setSplitToolbar(boolean split) {        ...    }    ...    public void setCustomView(View view) {        ...    }    ...    private void initTitle() {        ...    }    ...    public void initForMode(final ActionMode mode) {        ...    }}
  1. setSplitToolbar();判断此ActionBar是否需要split;跟ActionBarView的方法类似。

  2. setCustomView();移除里面的View,自定义View放置到ActionBarView上;

  3. initTitle();这个是初始化ActionBartitle的方法。使用LayoutInflater.inflate方法将title布局加载进来,并且初始化其中的TitleViewSubtitleView;
  4. initForMode();根据ActionMode初始化ActionBar;包括MenuViewSplitView
    好了,ActionBarContextView;的主要方法也就完了。
    最后我们通过图,来加深下印象,本来想自己画,但是看到网上有个好图就直接拿来了~
    Alt text

参考文章:Android ActionBar的源代码分析(二)这里的几个图都是这篇文章里的。

好了,setContentView,大致就分析完了,接下来我们继续分LayoutInflater,分析其时如何加载xml文件的。