zoukankan      html  css  js  c++  java
  • 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源码中,都能找到使用组合模式的例子,其中在《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()方法,有了增加孩子节点,肯定有相对应删除孩子节点的方法,接着看:

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

    四、基本控件继承关系图

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

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

  • 相关阅读:
    2017年5月24日 HTML 基础知识(二)
    2017年5月22日 HTML基础知识(一)
    尼采语录
    Unicode字符串和整数和浮点数
    转义字符
    python第一节
    C# ASP .NET WEB方向和WPF方向,我该如何去选择
    ORA-06550:line 1,column 7;PLS-00201:indentifer '存储过程' must be declared;...PL/SQL Statement ignored 问题
    C# WPF打印报表
    Sql Server 自定义数据类型
  • 原文地址:https://www.cnblogs.com/yemeishu/p/2847156.html
Copyright © 2011-2022 走看看