源码分析 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类来响应和储存状态
2.View会在setPress后产生按下状态
3.要实现图片的改变其实是改变View中的Drawable对象的状态,当view的状态改变,需要通知Drawable对象
4.接下来就是Drawable对象需要通过setState接收状态,同时通过重写onStateChange方法来找到正确的图片并绘制
5.View实现了Drawable用于绘制的回调接口
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); } } }
相关文章推荐
- Class类
- jQuery 标签后加提示信息
- 对I/O设备分配的一般策略是什么?
- 7月21日全球域名商(国际域名)解析量排行榜TOP20
- Win10 Mobile预览版10240模拟器上手演示视频
- 基于Spark构建推荐引擎
- Android Studio使用技巧系列教程(五)
- /var/spool/clientmqueue文件多导致磁盘满
- LeetCode之Interleaving String
- python 自己写爬虫 ---- 总结需要的包
- LNMP环境搭建
- 【jQuery】使用JQ要准备的主要淡入淡出效果
- 正则表达式在iOS中的运用
- cuda7.0+ caffe 小白安装手记
- string转换成int的算法
- hibernate实现NULLS LAST
- 详解Django框架中用户的登录和退出的实现
- Spring @Scheduled定时任务的简单使用
- python 自己写爬虫 ----- BeautifulSoup
- Eclipse免reload设置