广

android开发

  • IOS开发
  • android开发
  • PHP编程
  • JavaScript
  • ASP.NET
  • ASP编程
  • JSP编程
  • Java编程
  • 易语言
  • Ruby编程
  • Perl编程
  • AJAX
  • 正则表达式
  • C语言
  • 编程开发

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

    2018-04-04 08:34:58 次阅读 稿源:互联网
    广告

    组合模式定义

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

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

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

    一起学吧部分文章转载自互联网,供读者交流和学习,若有涉及作者版权等问题请及时与我们联系,以便更正、删除或按规定办理。感谢所有提供资讯的网站,欢迎各类媒体与一起学吧进行文章共享合作。

    广告
    广告
    广告