7.4.2 复合控件
复合控件(Compound Controls)是一种特殊的自定义控件。它派生自容器控件,在容器控件内部包含着一些预设的子控件,子控件和自定义的复合控件本身,共同构成一个整体,一起完成特定的业务逻辑。
在实际开发中,复合控件同样被广泛使用,尤其是在列表项的定制中。在Android原生的快速搜索应用中(如图7-15),搜索结果就是通过自定义的复合控件来呈现的。BaseSuggestionView是所有搜索结果控件的基类,它是一个自定义的复合控件:
public class BaseSuggestionView extends RelativeLayout implements
SuggestionView{
protected TextView mText1;//主文本控件
protected TextView mText2;//辅助文本控件
protected ImageView mIcon1;//主图标控件
protected ImageView mIcon2;//辅助图标控件
@Override
protected void onFinishInflate(){
//初始化并缓存控件对象
super.onFinishInflate();
mText1=(TextView)findViewById(R.id.text1);
mText2=(TextView)findViewById(R.id.text2);
mIcon1=(ImageView)findViewById(R.id.icon1);
mIcon2=(ImageView)findViewById(R.id.icon2);
}
}
图 7-15 快速搜索应用中的搜索结果控件
可以看到,BaseSuggestionView派生自关系容器控件RelativeLayout,其中包含两个文本控件和两个图像控件。在构造复合控件时,需要派生View.onFinishInflate函数并在其中对所包含的子控件进行初始化。这样的初始化工作,并不能在复合控件的构造函数中执行,因为那时子控件对象还不一定能够初始化完毕。而View.onFinishInflate函数的调用发生在界面控件均被构建完成之后,此时通过View.findViewById函数来寻找子控件是有保障的。
BaseSuggestionView封装了与搜索结果呈现相关的业务逻辑,这些业务逻辑需要父控件和子控件相互配合才能够实现:
//提供接口可以设置主文本信息
protected void setText1(CharSequence text){
mText1.setText(text);
}
//提供接口可以设置辅助文本信息
//当辅助文本信息为空时,辅助文本mText2控件消隐
protected void setText2(CharSequence text){
mText2.setText(text);
if(TextUtils.isEmpty(text)){
mText2.setVisibility(GONE);
}else{
mText2.setVisibility(VISIBLE);
}
}
//把搜索结果绑定到该控件对象上
public void bindAsSuggestion(Suggestion suggestion, String userQuery){
setOnClickListener(new ClickListener());
//如果是从历史记录中来的搜索结构,将复合控件设置为可长按
//反之,则不可长按
if(isFromHistory(suggestion)){
setLongClickable(true);
setOnLongClickListener(new LongClickListener());
}else{
setLongClickable(false);
setOnLongClickListener(null);
}
}
不难看出,对于类似于BaseSuggestionView这样的复合控件而言,最重要的事情是将一组与特定业务逻辑相关的控件封装在一起,控制其间的关系,共同支持业务逻辑的完成。对于复合控件而言,通常只是定义逻辑关系,而并不约束界面样式。界面样式是通过动态绑定资源文件而构建的。
这样的实现策略,使得界面样式的变化与业务逻辑的控制完全解耦。比如对于BaseSuggestionView控件而言,它并不关心其中的图标和文本如何排列,而只关注在没有辅助文本信息的时候将其隐藏,绑定历史搜索记录的时候支持长按。界面上的子控件排列和样式,可以随时根据不同的界面需求进行调整,提升了其复用性。