Android 组合模式(View与ViewGroup)
Android 设计模式系列文章 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的时候,约束起来就很麻烦。特别是类型多的时候。