首页 > 代码库 > Android -- selector&&StateListDrawable

Android -- selector&&StateListDrawable

selector                                                                               

<?xml version="1.0" encoding="utf-8" ?>     <selector xmlns:android="http://schemas.android.com/apk/res/android">     <!-- 触摸时并且当前窗口处于交互状态 -->      <item android:state_pressed="true" android:state_window_focused="true" android:drawable= "@drawable/pic1" />    <!--  触摸时并且没有获得焦点状态 -->      <item android:state_pressed="true" android:state_focused="false" android:drawable="@drawable/pic2" />      <!--选中时的图片背景-->      <item android:state_selected="true" android:drawable="@drawable/pic3" />       <!--获得焦点时的图片背景-->      <item android:state_focused="true" android:drawable="@drawable/pic4" />      <!-- 窗口没有处于交互时的背景图片 -->      <item android:drawable="@drawable/pic5" />   </selector>

StateListDrawable                                                                   

该类定义了不同状态值下与之对应的图片资源,即我们可以利用该类保存多种状态值,多种图片资源。

public void addState (int[] stateSet, Drawable drawable)

给特定的状态集合设置drawable图片资源,参考前面的selector的xml文件,我们利用代码去构建一个相同的StateListDrawable类对象

//初始化一个空对象  StateListDrawable stalistDrawable = new StateListDrawable();  //获取对应的属性值 Android框架自带的属性 attr  int pressed = android.R.attr.state_pressed;  int window_focused = android.R.attr.state_window_focused;  int focused = android.R.attr.state_focused;  int selected = android.R.attr.state_selected;    stalistDrawable.addState(new int []{pressed , window_focused}, getResources().getDrawable(R.drawable.pic1));  stalistDrawable.addState(new int []{pressed , -focused}, getResources().getDrawable(R.drawable.pic2);  stalistDrawable.addState(new int []{selected }, getResources().getDrawable(R.drawable.pic3);  stalistDrawable.addState(new int []{focused }, getResources().getDrawable(R.drawable.pic4);  //没有任何状态时显示的图片,我们给它设置我空集合  stalistDrawable.addState(new int []{}, getResources().getDrawable(R.drawable.pic5);

上面的“-”负号表示对应的属性值为false

public boolean isStateful ()

表明该状态改变了,对应的drawable图片是否会改变。(在StateListDrawable类中,该方法返回为true,显然状态改变后,我们的图片会跟着改变。)

状态                                                                                   

android:state_selected 选中
android:state_focused 获得焦点
android:state_pressed 点击
android:state_enabled 设置是否响应事件,指所有事件

selected和focused的区别                                                            

  1. selected不同于focus状态,通常在AdapterView类群下例如ListView或者GridView会使某个View处于selected状态,并且获得该状态的View处于高亮状态。
  2. 一个窗口只能有一个视图获得焦点(focus),而一个窗口可以有多个视图处于”selected”状态中。
  • focused状态一般是由按键操作引起的;
  • pressed状态是由触摸消息引起的;
  • selected则完全是由应用程序主动调用setSelected()进行控制。

分析                                                                                   

当View任何状态值发生改变时,都会调用refreshDrawableList()方法去更新对应的背景Drawable对象。

//主要功能是根据当前的状态值去更换对应的背景Drawable对象      public void refreshDrawableState() {          mPrivateFlags |= DRAWABLE_STATE_DIRTY;          //所有功能在这个函数里去完成          drawableStateChanged();          //...      }      // 获得当前的状态属性--- 整型集合 ; 调用Drawable类的setState方法去获取资源。      protected void drawableStateChanged() {          //该视图对应的Drawable对象,通常对应于StateListDrawable类对象          Drawable d = mBGDrawable;             if (d != null && d.isStateful()) {  //通常都是成立的              //getDrawableState()方法主要功能:会根据当前View的状态属性值,将其转换为一个整型集合              //setState()方法主要功能:根据当前的获取到的状态,更新对应状态下的Drawable对象。              d.setState(getDrawableState());          }      }      public final int[] getDrawableState() {          if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {              return mDrawableState;          } else {              //根据当前View的状态属性值,将其转换为一个整型集合,并返回              mDrawableState = onCreateDrawableState(0);              mPrivateFlags &= ~DRAWABLE_STATE_DIRTY;              return mDrawableState;          }      }

setState()

//如果状态态值发生了改变,就回调onStateChange()方法。  public boolean setState(final int[] stateSet) {      if (!Arrays.equals(mStateSet, stateSet)) {          mStateSet = stateSet;          return onStateChange(stateSet);      }      return false;  }

判断状态值是否发生了变化,如果发生了变化,就调用onStateChange()方法进一步处理。

//状态值发生了改变,我们需要找出第一个吻合的当前状态的Drawable对象  protected boolean onStateChange(int[] stateSet) {      //要找出第一个吻合的当前状态的Drawable对象所在的索引位置, 具体匹配算法请自己深入源码看看      int idx = mStateListState.indexOfStateSet(stateSet);      ...      //获取对应索引位置的Drawable对象      if (selectDrawable(idx)) {          return true;      }      ...  }

根据新的状态值,从StateListDrawable实例对象中,找到第一个完全吻合该新状态值的索引下标处 ; 继而,调用selectDrawable()方法去获取索引下标的当前Drawable对象。

public boolean selectDrawable(int idx)  {      if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {          //获取对应索引位置的Drawable对象          Drawable d = mDrawableContainerState.mDrawables[idx];          ...          mCurrDrawable = d; //mCurrDrawable即使当前Drawable对象          mCurIndex = idx;          ...      } else {         ...      }      //请求该View刷新自己,这个方法我们稍后讲解。      invalidateSelf();      return true;  }

Drawable.Callback                                                                    

public static interface Callback {      //如果Drawable对象的状态发生了变化,会请求View重新绘制,      //因此我们对应于该View的背景Drawable对象能够”绘制出来”.      public void invalidateDrawable(Drawable who);      public void scheduleDrawable(Drawable who, Runnable what, long when);       public void unscheduleDrawable(Drawable who, Runnable what);  }

Android框架View类继承了该接口,同时实现了这三个函数的默认处理方式,其中invalidateDrawable()方法如下:

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource   {      ...      //Invalidates the specified Drawable.      //默认实现,重新绘制该视图本身      public void invalidateDrawable(Drawable drawable) {          if (verifyDrawable(drawable)) { //是否是同一个Drawable对象,通常为真              final Rect dirty = drawable.getBounds();              final int scrollX = mScrollX;              final int scrollY = mScrollY;              //重新请求绘制该View,即重新调用该View的draw()方法  ...              invalidate(dirty.left + scrollX, dirty.top + scrollY,                      dirty.right + scrollX, dirty.bottom + scrollY);          }      }      ...  }

因此,我们的Drawable类对象必须将View设置为回调对象,否则,即使改变了状态,也不会显示对应的背景图。

Drawable d  ;                // 图片资源                        d.setCallback(View v) ;  // 视图v的背景资源为 d 对象

我是天王盖地虎的分割线                                                             

 

 

参考:http://blog.csdn.net/qinjuning/article/details/7474827

Android -- selector&&StateListDrawable