zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    每天备份数据库中的表
    IE9相容問題-childNodes行為改變
    查找含有某字符的存儲過程
    弹出SQL报表的时候,出现空白的解决方法
    转:Page.ClientScript.RegisterStartupScript(me.GetType(),"script1","<script>alert('111');</script>")
    java核心技术记录之java术语
    Head First 设计模式笔记:单例模式
    多对一关系实体中,多的一方所关联的一实体的实例化问题
    ajax请求返回json数据弹出下载框的解决方法
    java核心技术记录
  • 原文地址:https://www.cnblogs.com/yydcdut/p/4190284.html
Copyright © 2011-2022 走看看