Android中标签容器控件的实现
介绍
在一些APP中我们可以看到一些存放标签的容器控件,和我们平时使用的一些布局方式有些不同,它们一般都可以自动适应屏幕的宽度进行布局,根据对自定义控件的一些理解,今天写一个简单的标签容器控件,项目源码在最后给出
下面这个是我在手机上截取的一个实例,是在MIUI8系统上截取的
这个是我实现的效果图
原理介绍
根据对整个控件的效果分析,大致可以将控件分别从以下这几个角度进行分析:
1.首先涉及到自定义的ViewGroup,因为现有的控件没法满足我们的布局效果,就涉及到要重写onMeasure和onLayout,这里需要注意的问题是自定义View的时候,我们需要考虑到View的Padding属性,而在自定义ViewGroup中我们需要在onLayout中考虑Child控件的margin属性否则子类设置这个属性将会失效。整个View的绘制流程是这样的:
最顶层的ViewRoot执行performTraversals然后分别开始对各个View进行层级的测量、布局、绘制,整个流程是一层一层进行的,也就是说父视图测量时会调用子视图的测量方法,子视图调孙视图方法,一直测量到叶子节点,performTraversals这个函数翻译过来很直白,执行遍历,就说明了这种层级关系。
2.该控件形式上和ListView的形式比较相近,所以在这里我也模仿ListView的Adapter模式实现了对控件内容的操作,这里对ListView的setAdapter和Adapter的notifyDataSetChanged方法做个简单的解释:
在ListView调用setAdapter后,ListView会去注册一个Observer对象到这个adapter上,然后当我们在改变设置到adapter上的数据发改变时,我们会调用adapter的notifyDataSetChanged方法,这个方法就会通知所有监听了该Adapter数据改变时的Observer对象,这就是典型的监听者模式,这时由于ListView中的内部成员对象监听了该事件,就可以知道数据源发生了改变,我们需要对真个控件重新进行绘制了,下面来一些相关的源码。
Adapter的notifyDataSetChanged
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
ListView的setAdapter方法
@Override
public void setAdapter(ListAdapter adapter) {
/**
*每次设置新的适配的时候,如果现在有的话会做一个解除监听的操作
*/
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
/** 省略部分代码..... */
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
/**
*在这里对adapter设置了监听,
*使用的是AdapterDataSetObserver类的对象,该对象定义在ListView的父类AdapterView中
*/
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
/** 省略 */
} else {
/** 省略 */
}
requestLayout();
}
AdapterView中的内部类AdapterDataSetObserver
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
/* ***代码略*** */
checkFocus();
requestLayout();
}
@Override
public void onInvalidated() {
/* ***代码略*** */
checkFocus();
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}
一段伪代码表示
ListView{
Observer observer{
onChange(){
change;
}
}
setAdapter(Adapter adapter){
adapter.register(observer);
}
}
Adapter{
List<Observer> mObservable;
register(observer){
mObservable.add(observer);
}
notifyDataSetChanged(){
for(i-->mObserverable.size()){
mObserverable.get(i)