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的结构~
从这个结构图和上面的代码可以看出
ActionBar
和SplitActionBar
本质上为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
充当的是ActionBarView
和ActionBarContextView
的容器,而且这个容器在处理特别的ActionBar
风格的时候,也要实现动画切换。好了,这个容器不用多讲,我们继续往下走,看看ActionBarView
和ActionBarContextView
:
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();
的方法全都列了出来。咱们进行分析:
inProgress();
和initIndeterminateProgress();
这两个方法都是初始化进度条的方法。前一个是水平进度条,后一个是圆形进度条。两个方法相差不大,首先,new 一个ProgressBar
赋值给相应的申明的ProgressView
;其次设置ProgressView
的各种属性,最后addView
将此ProgressView
加入到ActionBar
.
setSplitToolbar();
判断此ActionBar
是否需要split
,里面是初始化SpliteActionBar
的方法;里面也有各种的判断,需要就addView
将其加入;
setEmbeddedTabView();
它负责的把Tab
容器添加到ActionBarView
中,当NavigationMode=NAVIGATION_MODE_TABS
(页签)时,这个方法才有效;传入的参数tabs
是一个ScrollingTabContainerView
类型的对象,ScrollingTabContainerView
继承于HorizontalScrollView
,可以看出它是一个具备水平滚动条的tab容器;当ActionBarPolicy
的方法hasEmbeddedTabs
返回true
时,tab
就会embed
到ActionBarView
上,否则就会放到ActionBarContainer
上,借用网上的一张图表示下这个方法的调用过程:
setMenu();
这个是设置ActionBar上菜单的方法,在这个方法中,系统会把menuView
放到ActionBar
中;
setCustomView();
这个方法是把自定义View
放置到ActionBarView
上;调用的过程借用网上的一张图来展示:
setDisplayOptions();
这个是设置ActionBar
显示选项的方法。根据不同的options
;设置不同的ActionBar
显示;setNavigationMode();
这是设置ActionBar导航模式的方法,对于mode的值,有三个值可选:NAVIGATION_MODE_STANDARD
(标准)、NAVIGATION_MODE_LIST
(列表)、NAVIGATION_MODE_TABS
(页签),当mode=NAVIGATION_MODE_LIST
时,就会把spinner
控件放置到ActionBarView
上;当mode=NAVIGATION_MODE_TABS
时,就会把TabScrollView
控件放置到ActionBarView
上;onFinishInflate();
这是对布局进行inflate
后回调的方法,在这个方法中,会把mHomeLayout
先添加到mUpGoerFive
,然后再把mUpGoerFive
放置到ActionBarView
中,这个mUpGoerFive
是一个ViewGroup
,它里面包含两个视图:mHomeLayout
和mTitleLayout
,其中mHomeLayout
包含两个ImageView:mUpView
(即返回的指示图标)和mIconView
(默认情况下是应用程序图标),这两个View
分别可以通过getActionBar().setDisplayHomeAsUpEnabled()
和getActionBar().setDisplayShowHomeEnabled()
来设置是否显示;而mTitleLayout
是一个LinearLayout
;initTitle();
这个是初始化ActionBar
的Title
的方法。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)
...
setSplitToolbar();
判断此ActionBar
是否需要split
;跟ActionBarView
的方法类似。
setCustomView();
移除里面的View
,自定义View
放置到ActionBarView
上;initTitle();
这个是初始化ActionBar
的title
的方法。使用LayoutInflater.inflate
方法将title
布局加载进来,并且初始化其中的TitleView
、SubtitleView
;initForMode()
;根据ActionMode
初始化ActionBar
;包括MenuView
、SplitView
;
好了,ActionBarContextView
;的主要方法也就完了。
最后我们通过图,来加深下印象,本来想自己画,但是看到网上有个好图就直接拿来了~
参考文章:Android ActionBar的源代码分析(二)这里的几个图都是这篇文章里的。
好了,setContentView,大致就分析完了,接下来我们继续分LayoutInflater,分析其时如何加载xml文件的。
以上是关于Android中View的相关知识的主要内容,如果未能解决你的问题,请参考以下文章