学习自http://blog.csdn.net/guolin_blog/article/details/17045157
5大状态
enabled
视图如果不可用:
1.onTouch得不到执行
2.onClick得不到执行
3.重写的onTouchEvent依然可以得到执行
(这3点可以从事件分发的源码中得到结论)
focused
requestFocus
在view不支持focused无效,不可见无效;
view的祖先view设置了FOCUS_BLOCK_DESCENDANTS下无效;
不支持Touch模式下获取焦点
requestFocusFromTouch可以移除Touch模式,并且获取焦点
(可以从这两个方法的源码中得到结论)
window_focused
selected
pressed
这样一个drawable,可以决定Button的按下,拥有焦点,正常状态下的背景图
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/compose_pressed" android:state_pressed="true"></item> <item android:drawable="@drawable/compose_pressed" android:state_focused="true"></item> <item android:drawable="@drawable/compose_normal"></item>
</selector>
原理
视图状态改变会回调
protected void drawableStateChanged() { Drawable d = mBGDrawable;//它是我们指定的,上面的drawable if (d != null && d.isStateful()) { d.setState(getDrawableState()); } }getDrawableState
public final int[] getDrawableState() { if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {//如果视图状态未改变(DIRTY) return mDrawableState; } else { mDrawableState = onCreateDrawableState(0);//获取当前视图状态 mPrivateFlags &= ~DRAWABLE_STATE_DIRTY; return mDrawableState; } }setState
public boolean setState(final int[] stateSet) { if (!Arrays.equals(mStateSet, stateSet)) { mStateSet = stateSet; return onStateChange(stateSet);//重置了视图状态,回调onStateChange } return false; }onStateChange
@Override protected boolean onStateChange(int[] stateSet) { int idx = mStateListState.indexOfStateSet(stateSet);//找到当前视图状态在上面那个selector对应状态的坐标 if (DEBUG) android.util.Log.i(TAG, "onStateChange " + this + " states " + Arrays.toString(stateSet) + " found " + idx); if (idx < 0) { idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD); } if (selectDrawable(idx)) {//选取背景图并设置为背景 return true; } return super.onStateChange(stateSet); }这里就进行了视图重绘(先留下一个疑问,什么时候会进行一个视图重绘)
调用视图的setVisibility()、setEnabled()、setSelected()等方法时都会导致视图重绘(他们内部也调用了invalidate)
如果我们想要手动地强制让视图进行重绘,可以调用invalidate()方法来实现。
invalidate是在原UI线程进行的,invalidatePost是在非UI线程进行的
invalidate
void invalidate(boolean invalidateCache) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); } if (skipInvalidate()) {//判断是否需要重绘,一般在有动画产生且可见,会进行一个重绘 return; } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) || (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~DRAWN; mPrivateFlags |= DIRTY; if (invalidateCache) { mPrivateFlags |= INVALIDATED; mPrivateFlags &= ~DRAWING_CACHE_VALID; } final AttachInfo ai = mAttachInfo; final ViewParent p = mParent;//当前view的父视图 if (!HardwareRenderer.RENDER_DIRTY_REGIONS) { if (p != null && ai != null && ai.mHardwareAccelerated) { p.invalidateChild(this, null); return; } } if (p != null && ai != null) { final Rect r = ai.mTmpInvalRect; r.set(0, 0, mRight - mLeft, mBottom - mTop); p.invalidateChild(this, r); } } }invalidateChild
public final void invalidateChild(View child, final Rect dirty) { ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; if (dirty == null) { ...... } else { ...... do {//这个for循环,找到所有parent,调用他们的invalidateChildInParent方法,这个方法主要用来计算需要重绘的矩形区域 View view = null; if (parent instanceof View) { view = (View) parent; if (view.mLayerType != LAYER_TYPE_NONE && view.getParent() instanceof View) { final View grandParent = (View) view.getParent(); grandParent.mPrivateFlags |= INVALIDATED; grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID; } } if (drawAnimation) { if (view != null) { view.mPrivateFlags |= DRAW_ANIMATION; } else if (parent instanceof ViewRootImpl) { ((ViewRootImpl) parent).mIsAnimating = true; } } if (view != null) { if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && view.getSolidColor() == 0) { opaqueFlag = DIRTY; } if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) { view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; } } parent = parent.invalidateChildInParent(location, dirty); if (view != null) { Matrix m = view.getMatrix(); if (!m.isIdentity()) { RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); m.mapRect(boundingRect); dirty.set((int) boundingRect.left, (int) boundingRect.top, (int) (boundingRect.right + 0.5f), (int) (boundingRect.bottom + 0.5f)); } } } while (parent != null); } } }invalidateChildInParent
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { invalidateChild(null, dirty); return null; }invalidateChild
public void invalidateChild(View child, Rect dirty) { checkThread(); if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty); mDirty.union(dirty); if (!mWillDrawSoon) { scheduleTraversals(); } }scheduleTraversals
public void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; sendEmptyMessage(DO_TRAVERSAL); } }ViewRoot.handleMessage
public void handleMessage(Message msg) { switch (msg.what) { case DO_TRAVERSAL: if (mProfile) { Debug.startMethodTracing("ViewRoot"); } performTraversals();//进行重新绘制 if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } break; ...... }回头看selectDrawable
public boolean selectDrawable(int idx) { if (idx == mCurIndex) { return false; } final long now = SystemClock.uptimeMillis(); if (mDrawableContainerState.mExitFadeDuration > 0) { if (mLastDrawable != null) { mLastDrawable.setVisible(false, false); } if (mCurrDrawable != null) { mLastDrawable = mCurrDrawable; mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration; } else { mLastDrawable = null; mExitAnimationEnd = 0; } } else if (mCurrDrawable != null) { mCurrDrawable.setVisible(false, false); } if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) { Drawable d = mDrawableContainerState.mDrawables[idx]; mCurrDrawable = d; mCurIndex = idx; if (d != null) { if (mDrawableContainerState.mEnterFadeDuration > 0) { mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration; } else { d.setAlpha(mAlpha); } d.setVisible(isVisible(), true); d.setDither(mDrawableContainerState.mDither); d.setColorFilter(mColorFilter); d.setState(getState()); d.setLevel(getLevel()); d.setBounds(getBounds()); } } else { mCurrDrawable = null; mCurIndex = -1; } if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) { if (mAnimationRunnable == null) { mAnimationRunnable = new Runnable() { @Override public void run() { animate(true); invalidateSelf(); } }; } else { unscheduleSelf(mAnimationRunnable); } animate(true); } invalidateSelf();//关键 return true; }invalidateSelf
public void invalidateSelf() { final Callback callback = getCallback(); if (callback != null) { callback.invalidateDrawable(this);//callback就是view,他自身 } }invalidate()方法虽然最终会调用到performTraversals()方法中,但这时measure和layout流程是不会重新执行的,因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。
而如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了
