Android,android官網
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類對象
//初始化一個Null 物件 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的區別
- 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