Android源码学习之组合模式定义及应用_安卓软件开发教程-查字典教程网
Android源码学习之组合模式定义及应用
Android源码学习之组合模式定义及应用
发布时间:2016-12-28 来源:查字典编辑
摘要:组合模式定义:Composeobjectsintotreestructurestorepresentpart-wholehierarchie...

组合模式定义:

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

如上图所示(截取自《Head First Design Patterns》一书),主要包括三个部分:

1. Component抽象组件。定义参加组合对象的共有方法和属性,可以定义一些默认的函数或属性。

2. Leaf叶子节点。构成组合树的最小构建单元。

3. Composite树枝节点组件。它的作用是组合树枝节点和叶子节点形成一个树形结构。

高层模块调用简单。一棵树形结构的所有节点都是Component,局部和整体对调用者来说都是一样的,没有区别,所以高层模块不比关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。

节点自由扩展增加。使用组合模式,如果想增加一个树枝节点或者叶子节点都是很简单的,只要找到它的父节点就可以了,非常容易扩展,符合“开闭原则”。

应用最广的模式之一。应用在维护和展示部分-整体关系的场景,如树形菜单、文件夹管理等等。

在Android源码中,都能找到使用组合模式的例子,其中在《Android源码学习之观察者模式应用》介绍到的ViewGroup和View的结构就是一个组合模式,结构图如下所示:

现在来看看它们是如何利用组合模式组织在一起的,首先在View类定义了有关具体操作,然后在ViewGroup类中继承View类,并添加相关的增加、删除和查找孩子View节点,代码如下:

复制代码 代码如下:

* @attr ref android.R.styleable#ViewGroup_clipChildren

* @attr ref android.R.styleable#ViewGroup_clipToPadding

* @attr ref android.R.styleable#ViewGroup_layoutAnimation

* @attr ref android.R.styleable#ViewGroup_animationCache

* @attr ref android.R.styleable#ViewGroup_persistentDrawingCache

* @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache

* @attr ref android.R.styleable#ViewGroup_addStatesFromChildren

* @attr ref android.R.styleable#ViewGroup_descendantFocusability

* @attr ref android.R.styleable#ViewGroup_animateLayoutChanges

*/

public abstract class ViewGroup extends View implements ViewParent, ViewManager {

接着看增加孩子节点函数:

复制代码 代码如下:

/**

* Adds a child view. If no layout parameters are already set on the child, the

* default parameters for this ViewGroup are set on the child.

*

* @param child the child view to add

*

* @see #generateDefaultLayoutParams()

*/

public void addView(View child) {

addView(child, -1);

}

/**

* Adds a child view. If no layout parameters are already set on the child, the

* default parameters for this ViewGroup are set on the child.

*

* @param child the child view to add

* @param index the position at which to add the child

*

* @see #generateDefaultLayoutParams()

*/

public void addView(View child, int index) {

LayoutParams params = child.getLayoutParams();

if (params == null) {

params = generateDefaultLayoutParams();

if (params == null) {

throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");

}

}

addView(child, index, params);

}

/**

* Adds a child view with this ViewGroup's default layout parameters and the

* specified width and height.

*

* @param child the child view to add

*/

public void addView(View child, int width, int height) {

final LayoutParams params = generateDefaultLayoutParams();

params.width = width;

params.height = height;

addView(child, -1, params);

}

/**

* Adds a child view with the specified layout parameters.

*

* @param child the child view to add

* @param params the layout parameters to set on the child

*/

public void addView(View child, LayoutParams params) {

addView(child, -1, params);

}

/**

* Adds a child view with the specified layout parameters.

*

* @param child the child view to add

* @param index the position at which to add the child

* @param params the layout parameters to set on the child

*/

public void addView(View child, int index, LayoutParams params) {

if (DBG) {

System.out.println(this + " addView");

}

// addViewInner() will call child.requestLayout() when setting the new LayoutParams

// therefore, we call requestLayout() on ourselves before, so that the child's request

// will be blocked at our level

requestLayout();

invalidate(true);

addViewInner(child, index, params, false);

}

在ViewGroup中我们找到了添加addView()方法,有了增加孩子节点,肯定有相对应删除孩子节点的方法,接着看:

复制代码 代码如下:

public void removeView(View view) {

removeViewInternal(view);

requestLayout();

invalidate(true);

}

/**

* Removes a view during layout. This is useful if in your onLayout() method,

* you need to remove more views.

*

* @param view the view to remove from the group

*/

public void removeViewInLayout(View view) {

removeViewInternal(view);

}

/**

* Removes a range of views during layout. This is useful if in your onLayout() method,

* you need to remove more views.

*

* @param start the index of the first view to remove from the group

* @param count the number of views to remove from the group

*/

public void removeViewsInLayout(int start, int count) {

removeViewsInternal(start, count);

}

/**

* Removes the view at the specified position in the group.

*

* @param index the position in the group of the view to remove

*/

public void removeViewAt(int index) {

removeViewInternal(index, getChildAt(index));

requestLayout();

invalidate(true);

}

/**

* Removes the specified range of views from the group.

*

* @param start the first position in the group of the range of views to remove

* @param count the number of views to remove

*/

public void removeViews(int start, int count) {

removeViewsInternal(start, count);

requestLayout();

invalidate(true);

}

private void removeViewInternal(View view) {

final int index = indexOfChild(view);

if (index >= 0) {

removeViewInternal(index, view);

}

}

private void removeViewInternal(int index, View view) {

if (mTransition != null) {

mTransition.removeChild(this, view);

}

boolean clearChildFocus = false;

if (view == mFocused) {

view.clearFocusForRemoval();

clearChildFocus = true;

}

if (view.getAnimation() != null ||

(mTransitioningViews != null && mTransitioningViews.contains(view))) {

addDisappearingView(view);

} else if (view.mAttachInfo != null) {

view.dispatchDetachedFromWindow();

}

onViewRemoved(view);

needGlobalAttributesUpdate(false);

removeFromArray(index);

if (clearChildFocus) {

clearChildFocus(view);

}

}

/**

* Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is

* not null, changes in layout which occur because of children being added to or removed from

* the ViewGroup will be animated according to the animations defined in that LayoutTransition

* object. By default, the transition object is null (so layout changes are not animated).

*

* @param transition The LayoutTransition object that will animated changes in layout. A value

* of <code>null</code> means no transition will run on layout changes.

* @attr ref android.R.styleable#ViewGroup_animateLayoutChanges

*/

public void setLayoutTransition(LayoutTransition transition) {

if (mTransition != null) {

mTransition.removeTransitionListener(mLayoutTransitionListener);

}

mTransition = transition;

if (mTransition != null) {

mTransition.addTransitionListener(mLayoutTransitionListener);

}

}

/**

* Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is

* not null, changes in layout which occur because of children being added to or removed from

* the ViewGroup will be animated according to the animations defined in that LayoutTransition

* object. By default, the transition object is null (so layout changes are not animated).

*

* @return LayoutTranstion The LayoutTransition object that will animated changes in layout.

* A value of <code>null</code> means no transition will run on layout changes.

*/

public LayoutTransition getLayoutTransition() {

return mTransition;

}

private void removeViewsInternal(int start, int count) {

final View focused = mFocused;

final boolean detach = mAttachInfo != null;

View clearChildFocus = null;

final View[] children = mChildren;

final int end = start + count;

for (int i = start; i < end; i++) {

final View view = children[i];

if (mTransition != null) {

mTransition.removeChild(this, view);

}

if (view == focused) {

view.clearFocusForRemoval();

clearChildFocus = view;

}

if (view.getAnimation() != null ||

(mTransitioningViews != null && mTransitioningViews.contains(view))) {

addDisappearingView(view);

} else if (detach) {

view.dispatchDetachedFromWindow();

}

needGlobalAttributesUpdate(false);

onViewRemoved(view);

}

removeFromArray(start, count);

if (clearChildFocus != null) {

clearChildFocus(clearChildFocus);

}

}

/**

* Call this method to remove all child views from the

* ViewGroup.

*/

public void removeAllViews() {

removeAllViewsInLayout();

requestLayout();

invalidate(true);

}

/**

* Called by a ViewGroup subclass to remove child views from itself,

* when it must first know its size on screen before it can calculate how many

* child views it will render. An example is a Gallery or a ListView, which

* may "have" 50 children, but actually only render the number of children

* that can currently fit inside the object on screen. Do not call

* this method unless you are extending ViewGroup and understand the

* view measuring and layout pipeline.

*/

public void removeAllViewsInLayout() {

final int count = mChildrenCount;

if (count <= 0) {

return;

}

final View[] children = mChildren;

mChildrenCount = 0;

final View focused = mFocused;

final boolean detach = mAttachInfo != null;

View clearChildFocus = null;

needGlobalAttributesUpdate(false);

for (int i = count - 1; i >= 0; i--) {

final View view = children[i];

if (mTransition != null) {

mTransition.removeChild(this, view);

}

if (view == focused) {

view.clearFocusForRemoval();

clearChildFocus = view;

}

if (view.getAnimation() != null ||

(mTransitioningViews != null && mTransitioningViews.contains(view))) {

addDisappearingView(view);

} else if (detach) {

view.dispatchDetachedFromWindow();

}

onViewRemoved(view);

view.mParent = null;

children[i] = null;

}

if (clearChildFocus != null) {

clearChildFocus(clearChildFocus);

}

}

/**

* Finishes the removal of a detached view. This method will dispatch the detached from

* window event and notify the hierarchy change listener.

*

* @param child the child to be definitely removed from the view hierarchy

* @param animate if true and the view has an animation, the view is placed in the

* disappearing views list, otherwise, it is detached from the window

*

* @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)

* @see #detachAllViewsFromParent()

* @see #detachViewFromParent(View)

* @see #detachViewFromParent(int)

*/

protected void removeDetachedView(View child, boolean animate) {

if (mTransition != null) {

mTransition.removeChild(this, child);

}

if (child == mFocused) {

child.clearFocus();

}

if ((animate && child.getAnimation() != null) ||

(mTransitioningViews != null && mTransitioningViews.contains(child))) {

addDisappearingView(child);

} else if (child.mAttachInfo != null) {

child.dispatchDetachedFromWindow();

}

onViewRemoved(child);

}

同样的,也有查找获得孩子节点的函数:

复制代码 代码如下:

/**

* Returns the view at the specified position in the group.

*

* @param index the position at which to get the view from

* @return the view at the specified position or null if the position

* does not exist within the group

*/

public View getChildAt(int index) {

if (index < 0 || index >= mChildrenCount) {

return null;

}

return mChildren[index];

}

注:其中具体叶子节点,如Button,它是继承TextView的,TextView是继承View的,代码如下:

复制代码 代码如下:

public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {

。。。

}

注:其中使用(继承)到ViewGroup类的有我们常用的容器类(包装和容纳各种View),如LinearLayout、FrameLayout等,代码如下:

复制代码 代码如下:

public class LinearLayout extends ViewGroup {

public static final int HORIZONTAL = 0;

public static final int VERTICAL = 1;

。。。

}

public class FrameLayout extends ViewGroup {

...

}

public class RelativeLayout extends ViewGroup {

private static final String LOG_TAG = "RelativeLayout";

private static final boolean DEBUG_GRAPH = false;

...

}

public class AbsoluteLayout extends ViewGroup {

public AbsoluteLayout(Context context) {

super(context);

}

}

...

最后送上“基本控件继承关系图”:

本人能力有限,写的很粗糙,恭候大家的批评指正,谢谢~~~

相关阅读
推荐文章
猜你喜欢
附近的人在看
推荐阅读
拓展阅读
  • 大家都在看
  • 小编推荐
  • 猜你喜欢
  • 最新安卓软件开发学习
    热门安卓软件开发学习
    编程开发子分类