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

组合模式定义

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);
}
}
...

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

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

时间: 2025-01-27 01:03:33

Android源码学习之组合模式定义及应用_Android的相关文章

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

组合模式定义: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性. 如上图所示(截取自<Head First De

Android源码学习之单例模式应用及优点介绍_Android

单例模式定义: Ensure a class has only one instance, and provide a global point of access to it. 动态确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.     如上图所示(截取自<Head First Design Patterns>一书). 通过使用private的构造函数确保了在一个应用中产生一个实例,并且是自行实例化(在Singleton中自己使用new Singleton()). 具体单

Android源码学习之工厂方法模式应用及优势介绍

工厂方法模式定义: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. 定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 常用的工厂方法模式结构: 如上图所示(截取自<Head Firs

Android源码学习之观察者模式应用及优点介绍

观察者模式定义: Define a one-to-many dependency between objects so that when one object changes state, all its dependents aer notified and updated automatically. 定义对象间一种一对多的依赖关系,使得当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新.   如上图所示(截取自<Head First Design Patterns>一书)

Android源码学习之单例模式应用及优点介绍

单例模式定义: Ensure a class has only one instance, and provide a global point of access to it. 动态确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 如上图所示(截取自<Head First Design Patterns>一书). 通过使用private的构造函数确保了在一个应用中产生一个实例,并且是自行实例化(在Singleton中自己使用new Singleton()). 具体单例模式有

如何学习android源码中的framework,只有java基础

问题描述 如何学习android源码中的framework,只有java基础 求大神指导,如何学习framework?源码里面东西太多,感觉找不到入口 解决方案 从写android应用软件开始,或者上网收集framework的一些讲解资料,网上特别多 解决方案二: 你怎么学习的java就怎么学习Android 解决方案三: 除了努力還是努力加油 解决方案四: 围绕AMS和WMS展开,Android系统核心就这两个,弄懂了这两个服务如何和应用交互的,基本就了解Android系统的运行原理了. 解决

《Android 源码设计模式解析与实战》——导读

目 录 自序一 自序二 前言 致谢 第1章 走向灵活软件之路--面向对象的六大原则 1.1节优化代码的第一步--单一职责原则1.2节让程序更稳定.更灵活--开闭原则1.3节构建扩展性更好的系统--里氏替换原则1.4节让项目拥有变化的能力--依赖倒置原则1.5节系统有更高的灵活性--接口隔离原则1.6节更好的可扩展性--迪米特原则1.7节总结 第2章 应用最广的模式--单例模式 2.1节单例模式介绍2.2节单例模式的定义2.3节单例模式的使用场景2.4节单例模式UML类图2.5节单例模式的简单示例

Android 源码分析,FreeMind 是一件超级利器

Android 源码分析,FreeMind 是一件超级利器 思维导图软件 XMind 与 FreeMind 的对比 作者: 善用佳软 日期: 2012-04-17 分类: 1 文本办公, 1.5 思维导图 标签: mindmap 思维导图类软件中,最有影响力的开源免费软件是 FreeMind 和 XMind.FreeMind历史悠久,当属经典:XMind作为后起之秀,大有赶超之势.同作为免费.开源的思维导图解决方案,应如何选择/结合两款软件?本文试做分析,以供用户/开发者参考. 本文的分析基于W

Android源码分析-Alarm机制与Binder的交互

转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/18448997 前言 本次给大家分析的是Android中Alarm的机制以及它和Binder的交互,所用源码为最新的Android4.4.因为Alarm的功能都是通过Binder来完成的,所以,介绍Alarm之前必须要先介绍下它是如何调用Binder来完成定时功能的.由于内容较多,本文会比较长,在文章结构安排上是这样的:首先简单介绍如何使用Alarm并给出其工作原理,接着分析