Android中View的相关知识

Posted Mario_oo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中View的相关知识相关的知识,希望对你有一定的参考价值。

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有关的了。附上手机屏幕层次图

我们进入此方法中:

//Creates a new ActionBar, locates the inflated ActionBarView, initializes the ActionBar with the view, and sets mActionBar
private 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的结构~

从这个结构图和上面的代码可以看出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上,借用网上的一张图表示下这个方法的调用过程:

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

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

  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;的主要方法也就完了。
    最后我们通过图,来加深下印象,本来想自己画,但是看到网上有个好图就直接拿来了~

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

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

以上是关于Android中View的相关知识的主要内容,如果未能解决你的问题,请参考以下文章

Android中View的相关知识

Android中View的相关知识

Android中view相关的知识

Android -- 自定义view实现keep欢迎页倒计时效果

Android中View的相关知识

Android中View的相关知识