您的位置:首页 > 其它

源码分析 selector 显示

2015-07-23 10:24 141 查看
selector原理简述过程:

1.drawable = new StateListDrawable();//android默认使用selector产生的Drawable对象类型是StateListDrawable

2.drawable.inflate(r, parser, attrs);//解析xml文件把各种状态储存进入drawable对象

3.setPressed(boolean pressed);//当View被点中调用,用户点击动作之后就是view产生状态的地方,改变一个现有状态参数为点中

4.onCreateDrawableState(0);//View生成状态集传递给drawable对象,参数不为0表示有自定义状态

5.onStateChange(stateSet);//drawable通过这个方法解析出一个index可以找到之前xml文件中存入对应的图片

6.invalidateSelf()//drawable通过这个方法重新绘制,其中包含一个Drawable.Callback接口

7.invalidateDrawable()//Drawable.Callback接口会调用这个方法进行重绘,在这里这个方法被view重写,调用了view的draw方法重绘

结论:

1.我们的Drawable类对象必须将View设置为回调对象,实现invalidateDrawable(Drawable drawable),让view负责绘制

2.Drawable对象可以解析xml文件并且储存各种状态和对应的图片

3.不能直接给Drawable指定状态,要通过onCreateDrawableState(0)返回的int[],Drawable对象从中可以解析出一个index来找到对应图片,也就是说Drawable的状态是和View相关联,状态值是View传递给它,而View的状态值是由一系列不同的操作,例如点击,选中产生。

4.由上所述,自定义状态和显示对应图片的步骤:

a.自定义xml文件selector,自定义状态对应图片,传递给drawable对象

b.在View中设定一个表明状态的参数mState

c.在View中类似onclick的操作响应方法中改变mState的状态

d.在View的onCreateDrawableState方法中,根据mState的状态判断是不是要添加额外的状态进入状态数组返回给Drawable对象

e.Drawable对象会在onStateChange方法中根据传入的状态和selector中的参数和对应的图片根据一些规则指定一个正确的图片进行显示

源码分析:

1.当按钮或其他组件状态改变时,怎么实现的相应状态图片或颜色的显示呢。首先Drawable对象会通过xml载入selector的信息,这样drawable对象就有了不同的状态和图片,由于Drawable是抽象类,为了响应不同的状态需要实现Drawable类

以下代码是当用selector作为Drawable传入view中, android默认定义了StateListDrawable类来响应和储存状态

[code]public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)  
    throws XmlPullParserException, IOException {  
        Drawable drawable;  

        final String name = parser.getName();  

        if (name.equals("selector")) {  
            drawable = new StateListDrawable();//看下这里  
        } else if (name.equals("level-list")) {  
            drawable = new LevelListDrawable();  
        } else if (name.equals("layer-list")) {  
            drawable = new LayerDrawable();  
        } else if (name.equals("transition")) {  
            drawable = new TransitionDrawable();  
        } else if (name.equals("color")) {  
            drawable = new ColorDrawable();  
        } else if (name.equals("shape")) {  
            drawable = new GradientDrawable();  
        } else if (name.equals("scale")) {  
            drawable = new ScaleDrawable();  
        } else if (name.equals("clip")) {  
            drawable = new ClipDrawable();  
        } else if (name.equals("rotate")) {  
            drawable = new RotateDrawable();  
        } else if (name.equals("animated-rotate")) {  
            drawable = new AnimatedRotateDrawable();              
        } else if (name.equals("animation-list")) {  
            drawable = new AnimationDrawable();  
        } else if (name.equals("inset")) {  
            drawable = new InsetDrawable();  
        } else if (name.equals("bitmap")) {  
            drawable = new BitmapDrawable();  
            if (r != null) {  
               ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());  
            }  
        } else if (name.equals("nine-patch")) {  
            drawable = new NinePatchDrawable();  
            if (r != null) {  
                ((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());  
             }  
        } else {  
            throw new XmlPullParserException(parser.getPositionDescription() +  
                    ": invalid drawable tag " + name);  
        }  

        drawable.inflate(r, parser, attrs);  
        return drawable;  
    }


2.View会在setPress后产生按下状态

[code]public void setPressed(boolean pressed) {  
        if (pressed) {  
            mPrivateFlags |= PRESSED;//PRESSED标志位置1  
        } else {  
            mPrivateFlags &= ~PRESSED;//PRESSED标志位置0  
        }  
        refreshDrawableState();//刷新Drawable状态  
        dispatchSetPressed(pressed);//分发setPress事件,Viewgroup重写了该方法,实现向子View的分发。  
    }


3.要实现图片的改变其实是改变View中的Drawable对象的状态,当view的状态改变,需要通知Drawable对象

[code]public void refreshDrawableState() {  
        mPrivateFlags |= DRAWABLE_STATE_DIRTY;//DRAWABLE_STATE_DIRTY标志位是干什么的呢,往下看就知道了  
        drawableStateChanged();//改变drawableState  

        ViewParent parent = mParent;  
        if (parent != null) {  
            parent.childDrawableStateChanged(this);//如果有父VIew的话,通知父View。  
        }  
    }  
[java] view plaincopyprint?
protected void drawableStateChanged() {  
        Drawable d = mBGDrawable;//我们设置的selector对应的Drawable,也就是StateListDrawable  
        if (d != null && d.isStateful()) {//StateListDrawable重写了该方法,返回true。  
            d.setState(getDrawableState());  
        }  
    }  
3.每个View通过onCreateDrawableState(0)来返回他目前的状态给Drawable对象
[java] view plaincopyprint?
public final int[] getDrawableState() {  
        if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {  
            return mDrawableState;  
        } else {  
            mDrawableState = onCreateDrawableState(0);  
            mPrivateFlags &= ~DRAWABLE_STATE_DIRTY;  
            return mDrawableState;  
        }  
    }

//这是onCreateDrawableState(0)的源码,主要作用是确定windows的状态,view是否能被显示,是不是焦点或按下的状态,然后把所有的状态产生一个int[]值返回,参数extraSpace是表明额外的状态,所以可以自定义状态,例如CompoundButton
protected int[] onCreateDrawableState(int extraSpace) {
        if ((mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE &&
                mParent instanceof View) {
   //返回父类的状态
            return ((View) mParent).onCreateDrawableState(extraSpace);
        }

        int[] drawableState;

        int privateFlags = mPrivateFlags;

//是否PRESSED状态
        int viewStateIndex = (((privateFlags & PRESSED) != 0) ? 1 : 0);

//是否ENABLED状态
        viewStateIndex = (viewStateIndex << 1)
                + (((mViewFlags & ENABLED_MASK) == ENABLED) ? 1 : 0);

//是否焦点状态
        viewStateIndex = (viewStateIndex << 1) + (isFocused() ? 1 : 0);

//是否选择状态
        viewStateIndex = (viewStateIndex << 1)
                + (((privateFlags & SELECTED) != 0) ? 1 : 0);

//view所在window是否获得焦点
        final boolean hasWindowFocus = hasWindowFocus();
        viewStateIndex = (viewStateIndex << 1) + (hasWindowFocus ? 1 : 0);

//通过以上的判断产生一个索引viewStateIndex,在VIEW_STATE_SETS中返回一个状态数组(所以VIEW_STATE_SETS是int[][]类型)
        drawableState = VIEW_STATE_SETS[viewStateIndex];

        //noinspection ConstantIfStatement
        if (false) {
            Log.i("View", "drawableStateIndex=" + viewStateIndex);
            Log.i("View", toString()
                    + " pressed=" + ((privateFlags & PRESSED) != 0)
                    + " en=" + ((mViewFlags & ENABLED_MASK) == ENABLED)
                    + " fo=" + hasFocus()
                    + " sl=" + ((privateFlags & SELECTED) != 0)
                    + " wf=" + hasWindowFocus
                    + ": " + Arrays.toString(drawableState));
        }

        if (extraSpace == 0) {
            return drawableState;
        }

//如果有额外的自定义状态就把它添加到状态数组中
        final int[] fullState;
        if (drawableState != null) {
            fullState = new int[drawableState.length + extraSpace];
            System.arraycopy(drawableState, 0, fullState, 0, drawableState.length);
        } else {
            fullState = new int[extraSpace];
        }

        return fullState;
}
//这个是CompoundButton对象重写的onCreateDrawableState方法,把额外的自定义CHECKED_STATE_SET状态添加进去
protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
        }
        return drawableState;
}


4.接下来就是Drawable对象需要通过setState接收状态,同时通过重写onStateChange方法来找到正确的图片并绘制

[code]public boolean setState(final int[] stateSet) {  
        if (!Arrays.equals(mStateSet, stateSet)) {  
            mStateSet = stateSet;  
            return onStateChange(stateSet);//StateListDrawable重写了onStateChange。  
        }  
        return false;  
    }  
[java] view plaincopyprint?
@Override  
    //看StateListDrawable的onStateChange是怎么实现的,选择了一个正确的图片
    protected boolean onStateChange(int[] stateSet) {  
        int idx = mStateListState.indexOfStateSet(stateSet);//根据状态集匹配一个索引值  
        if (idx < 0) {  
            idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);  
        }  
        if (selectDrawable(idx)) {//根据索引值能找到相应状态集对应的Drawable对象。  
            return true;  
        }  
        return super.onStateChange(stateSet);  
    }  
[java] view plaincopyprint?
public boolean selectDrawable(int idx)  
    {  
        if (idx == mCurIndex) {  
            return false;  
        }  
        if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {  
            Drawable d = mDrawableContainerState.mDrawables[idx];  
            if (mCurrDrawable != null) {  
                mCurrDrawable.setVisible(false, false);  
            }  
            mCurrDrawable = d;//把当前状态集对应的Drawable对象赋值给mCurrDrawable  
            mCurIndex = idx;  
            if (d != null) {  
                d.setVisible(isVisible(), true);  
                d.setAlpha(mAlpha);  
                d.setDither(mDrawableContainerState.mDither);  
                d.setColorFilter(mColorFilter);  
                d.setState(getState());  
                d.setLevel(getLevel());  
                d.setBounds(getBounds());  
            }  
        } else {  
            if (mCurrDrawable != null) {  
                mCurrDrawable.setVisible(false, false);  
            }  
            mCurrDrawable = null;  
            mCurIndex = -1;  
        }  
        invalidateSelf();//最终调用这个方法。下面看看这个方法。  
        return true;  
    }  
[java] view plaincopyprint?
public void invalidateSelf()  
    {  
        if (mCallback != null) {  
            mCallback.invalidateDrawable(this);  
        }  
    }


5.View实现了Drawable用于绘制的回调接口

[code]public void setBackgroundDrawable(Drawable d) {  
        boolean requestLayout = false;  

        mBackgroundResource = 0;  

        if (mBGDrawable != null) {  
            mBGDrawable.setCallback(null);  
            unscheduleDrawable(mBGDrawable);  
        }  

        if (d != null) {  
            Rect padding = sThreadLocal.get();  
            if (padding == null) {  
                padding = new Rect();  
                sThreadLocal.set(padding);  
            }  
            if (d.getPadding(padding)) {  
                setPadding(padding.left, padding.top, padding.right, padding.bottom);  
            }  

            // Compare the minimum sizes of the old Drawable and the new.  If there isn't an old or  
            // if it has a different minimum size, we should layout again  
            if (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() ||  
                    mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) {  
                requestLayout = true;  
            }  

            d.setCallback(this);//这里设置了CallBack,View实现了Drawable.Callback接口。  
            if (d.isStateful()) {  
                d.setState(getDrawableState());  
            }  
            d.setVisible(getVisibility() == VISIBLE, false);  
            mBGDrawable = d;  

            if ((mPrivateFlags & SKIP_DRAW) != 0) {  
                mPrivateFlags &= ~SKIP_DRAW;  
                mPrivateFlags |= ONLY_DRAWS_BACKGROUND;  
                requestLayout = true;  
            }  
        } else {  
            /* Remove the background */  
            mBGDrawable = null;  

            if ((mPrivateFlags & ONLY_DRAWS_BACKGROUND) != 0) {  

                mPrivateFlags &= ~ONLY_DRAWS_BACKGROUND;  
                mPrivateFlags |= SKIP_DRAW;  
            }  

            requestLayout = true;  
        }  

        computeOpaqueFlags();  

        if (requestLayout) {  
            requestLayout();  
        }  

        mBackgroundSizeChanged = true;  
        invalidate();  
    }  
[java] view plaincopyprint?
//View中invalidateDrawable的实现
public void invalidateDrawable(Drawable drawable) {  
        if (verifyDrawable(drawable)) {  
            final Rect dirty = drawable.getBounds();  
            final int scrollX = mScrollX;  
            final int scrollY = mScrollY;  

            invalidate(dirty.left + scrollX, dirty.top + scrollY,  
                    dirty.right + scrollX, dirty.bottom + scrollY);//最终会申请失效,导致重新绘制。  
        }  
    }  
[java] view plaincopyprint?
//View调用draw绘制
public void draw(Canvas canvas) {  

        final int privateFlags = mPrivateFlags;  
        final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&  
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);  
        mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN;  

        // Step 1, draw the background, if needed  
        int saveCount;  

        if (!dirtyOpaque) {  
            final Drawable background = mBGDrawable;  
            if (background != null) {  
                final int scrollX = mScrollX;  
                final int scrollY = mScrollY;  

                if (mBackgroundSizeChanged) {  
                    background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);  
                    mBackgroundSizeChanged = false;  
                }  

                if ((scrollX | scrollY) == 0) {  
                    background.draw(canvas);//最终会调用background的draw()  
                } else {  
                    canvas.translate(scrollX, scrollY);  
                    background.draw(canvas);  
                    canvas.translate(-scrollX, -scrollY);  
                }  
            }  
        }  
[java] view plaincopyprint?
//StateListDrawable的父类DrawableContainer的draw()
@Override  
    public void draw(Canvas canvas) {  
        if (mCurrDrawable != null) {  
            mCurrDrawable.draw(canvas);  
        }  
    }  
[java] view plaincopyprint?
//mCurrDrawable是一个BitmapDrawable类型的对象。看一下BitmapDrawable的draw()
@Override  
    public void draw(Canvas canvas) {  
        Bitmap bitmap = mBitmap;  
        if (bitmap != null) {  
            final BitmapState state = mBitmapState;  
            if (mRebuildShader) {  
                Shader.TileMode tmx = state.mTileModeX;  
                Shader.TileMode tmy = state.mTileModeY;  

                if (tmx == null && tmy == null) {  
                    state.mPaint.setShader(null);  
                } else {  
                    Shader s = new BitmapShader(bitmap,  
                            tmx == null ? Shader.TileMode.CLAMP : tmx,  
                            tmy == null ? Shader.TileMode.CLAMP : tmy);  
                    state.mPaint.setShader(s);  
                }  
                mRebuildShader = false;  
                copyBounds(mDstRect);  
            }  

            Shader shader = state.mPaint.getShader();  
            if (shader == null) {  
                if (mApplyGravity) {  
                    Gravity.apply(state.mGravity, mBitmapWidth, mBitmapHeight,  
                            getBounds(), mDstRect);  
                    mApplyGravity = false;  
                }  
                canvas.drawBitmap(bitmap, null, mDstRect, state.mPaint);//最终通过canvas绘制这张图片。  
            } else {  
                if (mApplyGravity) {  
                    mDstRect.set(getBounds());  
                    mApplyGravity = false;  
                }  
                canvas.drawRect(mDstRect, state.mPaint);  
            }  
        }  
    }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: