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的状态

protectedvoiddrawableStateChanged(){

Drawabled=mBGDrawable;

if(d!=null&&d.isStateful()){

d.setState(getDrawableState());

}

}

然后会调用invalidateDrawable这个回调函数来刷新界面,同时调用draw函数实现绘制。

再次我们来看实现Selector功能的Drawable即StateListDrawable是如何实现Selector功能的。

上面我们己经看到在View状态改变的时候,会调用Drawable的setState函数。在Drawable中是这样实现setState的

publicbooleansetState(finalint[]stateSet){

if(!Arrays.equals(mStateSet,stateSet)){

mStateSet=stateSet;

returnonStateChange(stateSet);

}

returnfalse;

}

它在改变状态的时候会调用onStateChage来通知状态己经改变了。而StateListDrawable是继承Drawable的子类它复写了onStateChage函数

protectedbooleanonStateChange(int[]stateSet){

intidx=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)){

returntrue;

}

returnsuper.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函数,StateListDrawabledraw的是当前状态对应的drawable。


更多相关文章

  1. 关于基本控件EditText属性大全详解
  2. Android判断网络状态方法详解
  3. 学习:Android常用控件
  4. Android Studio如何使用快捷键生成get,set,tostring,构造函数
  5. android中的timepick控件简单实用
  6. Android UI 与文本相关的控件
  7. 【摘录】Android 2.3状态栏中添加menu,home和back快捷键
  8. ImageView图像控件之缩放和旋转

随机推荐

  1. 安卓开发入门-与java关系
  2. Android(安卓)UI开发第二十五篇——分享
  3. android Lru图片缓存管理方案
  4. Android上鲜为人知的UI控件介绍和使用
  5. Android(安卓)context 文件模式
  6. Android的性能优化方法
  7. Android(安卓)进程保活,Service进程常驻
  8. Android数据库操作--greenDAO的入门使用
  9. Android(安卓)如何在关于手机界面添加个
  10. 不需要任何权限获得Android设备的唯一ID