学习自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()了