Android 组合模式(View与ViewGroup)

Android 23种设计模式

前言

组合设计模式,又被称为部分整体模式。组合模式就是把一组比较相似的对象当做一样的对象处理。并根据树状结构来组合对象,然后提供可以一个统一方法去访问这些对象,这样就可以忽略对象和集合之间的差别。




我们可以看下这两张树状图。公司架构图里边,树状图的各个节点node,还有各个叶子leaf实际上他们是由差别的。而组合模式就是把node当做一样的对象处理。leaf也当做一样的对象处理。而当我们要对node和leaf操作的时候,不需要考虑他是节点还是叶子,组合模式提供一致的方式来操作。这就是组合模式了。

组合模式定义

将部分整体的层次结构转换为树状结构,是的客户访问对象和组合对象具有一致性。

组合模式举例

组合模式在写法上分为透明组合模式和安全组合模式。我们先来透明组合模式

透明组合模式

1、先抽象出方法

public abstract class Component {
    String name;
    public Component(String name){
        this.name = name;
    }
    public abstract void print();
    public abstract void addChild(Component component);
    public abstract void removeChild(Component component);
    public abstract Component getChild(int index);
}

2、node节点
由于root根节点根node几乎一样,这里就直接定义node未单独定义一个root节点

public class Node extends Component {
    private static final String TAG = Node.class.getSimpleName();
    private List<Component> list = new ArrayList<>();

    public Node(String name) {
        super(name);
    }

    @Override
    public void print() {
        Log.d(TAG,name);
        for (Component component:list) {
            component.print();
        }
    }

    @Override
    public void addChild(Component component) {
        list.add(component);
    }

    @Override
    public void removeChild(Component component) {
        list.remove(component);
    }

    @Override
    public Component getChild(int index) {
        return list.get(index);
    }
}

3、叶子
枝干很简单就是实现我们的增删查和遍历。然后看叶子

public class Leaf extends Component {
    private static final String TAG = Leaf.class.getSimpleName();

    public Leaf(String name) {
        super(name);
    }

    @Override
    public void print() {
        Log.d(TAG,name);
    }

    @Override
    public void addChild(Component component) {
        Log.d(TAG,"叶子节点,没有子节点");
    }

    @Override
    public void removeChild(Component component) {
        Log.d(TAG,"叶子节点,没有子节点");
    }

    @Override
    public Component getChild(int index) {
        Log.d(TAG,"叶子节点,没有子节点");
        return null;
    }
}

4、调用
由于叶子节点没有子节点了,所以增删查询就没有作用了。接下来调用

        Component root = new Node("XX公司");
        Component software = new Node("软件部");
        Component hardware = new Node("硬件部");

        Component androidSoftware = new Leaf("android");
        Component iosSoftware = new Leaf("ios");
        Component layout = new Leaf("layout");

        root.addChild(software);
        root.addChild(hardware);
        software.addChild(androidSoftware);
        software.addChild(iosSoftware);
        hardware.addChild(layout);

        root.print();

5、打印
上也说了由于node和root极其相似,所以就复用了。看输出

01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Node: XX公司
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Node: 软件部
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Leaf: android
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Leaf: ios
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Node: 硬件部
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Leaf: layout

到此,我想你已经理解什么是组合模式了。这时候有的读者可能发现了,叶子节点很多方法没必要存在,如果方法多的时候,代码就十分繁琐多于。这个时候另外一种组合模式的写法就出来了。

安全组合模式

1、抽象方法
安全组合模式和上面的透明组合模式最大差别呢就是把抽象方法简化了,只留了我们都需要的。

public abstract class SafeComponent {
    protected String name;
    public SafeComponent(String name) {
        this.name = name;
    }
    public abstract void print();
}

2、node
增删查不再是统一接口

public class SafeNode extends SafeComponent {
    private static final String TAG = SafeNode.class.getSimpleName();
    private List<SafeComponent> list = new ArrayList<>();

    public SafeNode(String name) {
        super(name);
    }

    @Override
    public void print() {
        Log.d(TAG,name);
        for (SafeComponent safeComponent:list) {
            safeComponent.print();
        }
    }

    public void addChild(SafeComponent safeComponent) {
        list.add(safeComponent);
    }

    public void removeChild(SafeComponent safeComponent) {
        list.remove(safeComponent);
    }

    public SafeComponent getChild(int index) {
        return list.get(index);
    }
}

3、leaf

public class SafeLeaf extends SafeComponent {
    private static final String TAG = SafeLeaf.class.getSimpleName();

    public SafeLeaf(String name) {
        super(name);
    }

    @Override
    public void print() {
        Log.d(TAG,name);
    }
}

4、调用

        SafeComponent root = new SafeNode("XX公司");
        SafeComponent software = new SafeNode("软件部");
        SafeComponent hardware = new SafeNode("硬件部");

        SafeComponent androidSoftware = new SafeLeaf("android");
        SafeComponent iosSoftware = new SafeLeaf("ios");
        SafeComponent layout = new SafeLeaf("layout");

        ((SafeNode) root).addChild(software);
        ((SafeNode) root).addChild(hardware);
        ((SafeNode) software).addChild(androidSoftware);
        ((SafeNode) software).addChild(iosSoftware);
        ((SafeNode) hardware).addChild(layout);

        root.print();

调用过后输出和上面是一样的,这里就不重复了。安全组合模式分工就很明确了。它还有一个好处就是当我们add/remove/getchild的时候,我们能知道具体的类是什么了,而透明组合模式就得在运行时去判断,比较麻烦。

View和ViewGroup

1、View和ViewGroup就是安全组合容器

组合模式Android中最经典的用法无非就是View和ViewGroup的运用了。我们都知道View还有Textview、Button他们都是继承于View。他们就像叶子。而ViewGroup是容器,ViewGroup可以添加View,而View不能添加ViewGroup。这不就是安全组合模式里边Leaf和Node的关系吗。所以它就是安全组合模式的一种运用。

2、ViewGroup容器实现方式

2.1、接口添加操作子视图方法

public abstract class ViewGroup extends View implements ViewParent, ViewManager {

首先ViewGroup继承自View,所有ViewGroup拥有view的公开方法。具有view的特性。然后ViewGroup继承了两个接口。我们先看ViewManager这个接口

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

一眼就明白了,跟我们之前的node一样,添加了add/remove等对子视图的操作方法。
在看ViewParent

public interface ViewParent {
    public void requestLayout();
    public void invalidateChild(View child, Rect r);
    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset);
    public void requestChildFocus(View child, View focused);
    ...
}

这里省略了ViewParent其它接口,我们会看到我们很多熟悉的方法,请求重新布局、获取子视图焦点等等。这里也是一些对子视图的操作。

2.2、继承View复写onalyout操作子元素

ViewGroup是一个抽象类,通篇ViewGroup的代码,ViewGroup就一个为了把View的onlayout方法重置为抽象方法。为啥View要这么做呢?我们知道View的onlatyou的方法是,View在父视图中layout布局过后调用onlayout方法,onlayout只是一个空实现,因为View已经布局完成。onlayout没有其他的实现意义。而ViewGroup作为一个视图容器,ViewGroup调用layout布局完自己后。还需要布局子视图。所以它把onlayout重置为抽象方法, 让子类必须实现。

2.3、其他方法

除了onlayout还有一些其他方法。我们再举一个例子。
View中的dispatchDraw是一个空实现,而ViewGroup中就是为了遍历然后drawChild。等等还有其他方法都是为了操作子视图 。这不就是组合模式么。

 protected void dispatchDraw(Canvas canvas) {
    ...
    for (int i = 0; i < childrenCount; i++) {
            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
                final View transientChild = mTransientViews.get(transientIndex);
                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                        transientChild.getAnimation() != null) {
                    more |= drawChild(canvas, transientChild, drawingTime);
                }
                transientIndex++;
                if (transientIndex >= transientCount) {
                    transientIndex = -1;
                }
            }

            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);
            }
        }
      ....
}

总结

现在我想大家对什么是组合模式已经有杆秤了。最后我想说下组合模式的优缺点
优点:
1、组合模式可以以层次结构清楚的定义复杂对象。让高层次忽略层次差异。
2、高层次可以使用统一的方法而不用担心它是枝干还是叶子。比如ViewGroup的dispatchView
3、组合模式可以形成复杂的树形结构,但对树形机构的控制却非常简单。
缺点:
1、叶子类型不能控制。比如我想控制ViewGroup添加的View必须为TextView的时候,约束起来就很麻烦。特别是类型多的时候。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容