首页 > 系统 > Android > 正文

Android源码学习之组合模式定义及应用

2019-10-24 21:08:23
字体:
来源:转载
供稿:网友
将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性,需要了解的朋友可以参考下
 

组合模式定义

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

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

Android源码学习之组合模式定义及应用

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

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

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

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

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

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

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

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

Android源码学习之组合模式定义及应用

现在来看看它们是如何利用组合模式组织在一起的,首先在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); 


...

最后送上“基本控件继承关系图”:
Android源码学习之组合模式定义及应用

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


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表