丈夫欲遂平生志,一载寒窗一举汤。这篇文章主要讲述Android Selector原理相关的知识,希望能为你提供帮助。
android的selector对于android开发者而言再熟悉不过了,只要定义一个drawable目录下定义一个selector的xml文件,在布局文件中引用这个xml文件或者在代码中setBackgroundDrawable的时候使用此xml就可以实现控件按下或有焦点等不同状态的效果。
那么setBackgroundDrawable后为什么可以实现这个功能呢?
首先要了解一个Drawable类,Drawable是一个抽象的可绘制的图片类,这个类可以从一个本地路径中创建一个图片,也可以使用从定义好的xml中创建,他们分别对应Drawable的createFromPath和createFromXml函数,其中createFromPath是从路径中创建一个Bitmap对象并将它转换成BitmapDrawable,而createFromXml是从xml中定义的标签,例如selector的话就创建StateListDrawable对象,shape的话就创建GradientDrawable对象,color的话就创建ColorDrawable......而BitmapDrawable、StateListDrawable、GradientDrawable都是从Drawable类中派生而来。其中StateListDrawable类就是实现selector中定义的样式的Drawable.
其次我们看Drawable怎么跟View关联的。
Drawable类有维护了一个控件的不同状态的变量mStateSet,当View.setBackgroundDrawable时,会调用Drawable的isStateful函数判断是否有不同状态的,StateListDrawable返回的true,如果是有状态的就会将view的状态赋值给drawable即d.setState(getDrawableState());
if
(d.isStateful())
{
d.setState(getDrawableState());
}
同时将传入的Drawable作为背景的Drawable.当控件接收到touch事件时会调用refreshDrawableState更新控件状态,同时也会更新背景的Drawable的状态
protected
void
drawableStateChanged()
{
Drawable
d
=
mBGDrawable;
if
(d
!=
null
&
&
d.isStateful())
{
d.setState(getDrawableState());
}
}
然后会调用invalidateDrawable这个回调函数来刷新界面,同时调用draw函数实现绘制。
再次我们来看实现Selector功能的Drawable即StateListDrawable是如何实现Selector功能的。
上面我们己经看到在View状态改变的时候,会调用Drawable的setState函数。在Drawable中是这样实现setState的
public
boolean
setState(final
int[]
stateSet)
{
if
(!Arrays.equals(mStateSet,
stateSet))
{
【Android Selector原理】
mStateSet
=
stateSet;
return
onStateChange(stateSet);
}
return
false;
}
它在改变状态的时候会调用onStateChage来通知状态己经改变了。而StateListDrawable是继承Drawable的子类它复写了onStateChage函数
protected
boolean
onStateChange(int[]
stateSet)
{
int
idx
=
mStateListState.indexOfStateSet(stateSet);
if
(DEBUG)
android.util.Log.i(TAG,
"onStateChange
"
+
this
+
"
states
"
+
Arrays.toString(stateSet)
+
"
found
"
+
idx);
if
(idx
<
0)
{
idx
=
mStateListState.indexOfStateSet(StateSet.WILD_CARD);
}
if
(selectDrawable(idx))
{
return
true;
}
return
super.onStateChange(stateSet);
}
从上面的实现可以看到它在改变状态的时候会调用selectDrawable来选择一个当前状态的drawable,这就是实现的关键了。StateListDrawable继承了DrawableContainer而DrawableContainer继承了Drawable,StateListState是StateListDrawable的内部类,它就是保存selector中定义的不同状态的drawable的实现,它提供了addStateSet函数来增加某个状态下对应的drawable对象并将它保存在mStateSets变量中,而indexOfStateSet函数则是查找某个状态下对应的drawable。selectDrawable是DrawableContainer的类,它是根据传入的状态的索引来找到对应的drawable来当作当前状态下的drawable。
OK,现我我们终于能理解为什么selector是如何实现不同状态不同样式了。View使用Drawable来实现背景图,selector对应StateListDrawable,当view状态改变时,会改变drawable的状态,StateListDrawable在改变状态时会根据当前状态选择对应的drawable,这样在view绘制时会调用drawable的draw函数,StateListDrawable
draw的是当前状态对应的drawable。
推荐阅读
- Android图形显示之硬件抽象层Gralloc
- Window Application has "update" key words
- Android 8.0 AlarmManager 后台定时任务
- Android代码混淆
- 如何让你的应用程序在App Store中脱颖而出
- NetAppSnapDrive安装过程中遇到的一些报错
- Perl chop()和chomp()
- PDFBox处理元数据
- 带循环的Perl数组