欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > 内容正文

Android视图重绘,android视图

来源: 开发者 投稿于  被查看 746 次 评论:252

Android视图重绘,android视图


学习自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>  


原理

视图状态改变会回调

  1. protected void drawableStateChanged() {  
  2.     Drawable d = mBGDrawable;//它是我们指定的,上面的drawable  
  3.     if (d != null && d.isStateful()) {  
  4.         d.setState(getDrawableState());  
  5.     }  
  6. }  

getDrawableState

  1. public final int[] getDrawableState() {  
  2.     if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {//如果视图状态未改变(DIRTY)  
  3.         return mDrawableState;  
  4.     } else {  
  5.         mDrawableState = onCreateDrawableState(0);//获取当前视图状态  
  6.         mPrivateFlags &= ~DRAWABLE_STATE_DIRTY;  
  7.         return mDrawableState;  
  8.     }  
  9. }

setState

  1. public boolean setState(final int[] stateSet) {  
  2.     if (!Arrays.equals(mStateSet, stateSet)) {  
  3.         mStateSet = stateSet;  
  4.         return onStateChange(stateSet);//重置了视图状态,回调onStateChange  
  5.     }  
  6.     return false;  
  7. }

onStateChange

  1. @Override  
  2. protected boolean onStateChange(int[] stateSet) {  
  3.     int idx = mStateListState.indexOfStateSet(stateSet);//找到当前视图状态在上面那个selector对应状态的坐标
  4.     if (DEBUG) android.util.Log.i(TAG, "onStateChange " + this + " states "  
  5.             + Arrays.toString(stateSet) + " found " + idx);  
  6.     if (idx < 0) {  
  7.         idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD); 
  8.     }  
  9.     if (selectDrawable(idx)) {//选取背景图并设置为背景  
  10.         return true;  
  11.     }  
  12.     return super.onStateChange(stateSet);  
  13. }  


这里就进行了视图重绘(先留下一个疑问,什么时候会进行一个视图重绘)

调用视图的setVisibility()、setEnabled()、setSelected()等方法时都会导致视图重绘(他们内部也调用了invalidate)

如果我们想要手动地强制让视图进行重绘,可以调用invalidate()方法来实现。

invalidate是在原UI线程进行的,invalidatePost是在非UI线程进行的

invalidate

  1. void invalidate(boolean invalidateCache) {  
  2.     if (ViewDebug.TRACE_HIERARCHY) {  
  3.         ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);  
  4.     }  
  5.     if (skipInvalidate()) {//判断是否需要重绘,一般在有动画产生且可见,会进行一个重绘  
  6.         return;  
  7.     }  
  8.     if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||  
  9.             (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) ||  
  10.             (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) {  
  11.         mLastIsOpaque = isOpaque();  
  12.         mPrivateFlags &= ~DRAWN;  
  13.         mPrivateFlags |= DIRTY;  
  14.         if (invalidateCache) {  
  15.             mPrivateFlags |= INVALIDATED;  
  16.             mPrivateFlags &= ~DRAWING_CACHE_VALID;  
  17.         }  
  18.         final AttachInfo ai = mAttachInfo;  
  19.         final ViewParent p = mParent;//当前view的父视图  
  20.         if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {  
  21.             if (p != null && ai != null && ai.mHardwareAccelerated) {  
  22.                 p.invalidateChild(thisnull);  
  23.                 return;  
  24.             }  
  25.         }  
  26.         if (p != null && ai != null) {  
  27.             final Rect r = ai.mTmpInvalRect;  
  28.             r.set(00, mRight - mLeft, mBottom - mTop);  
  29.             p.invalidateChild(this, r);  
  30.         }  
  31.     }  
  32. }  

invalidateChild

  1. public final void invalidateChild(View child, final Rect dirty) {  
  2.     ViewParent parent = this;  
  3.     final AttachInfo attachInfo = mAttachInfo;  
  4.     if (attachInfo != null) {  
  5.         final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;  
  6.         if (dirty == null) {  
  7.             ......  
  8.         } else {  
  9.             ......  
  10.             do {//这个for循环,找到所有parent,调用他们的invalidateChildInParent方法,这个方法主要用来计算需要重绘的矩形区域  
  11.                 View view = null;  
  12.                 if (parent instanceof View) {  
  13.                     view = (View) parent;  
  14.                     if (view.mLayerType != LAYER_TYPE_NONE &&  
  15.                             view.getParent() instanceof View) {  
  16.                         final View grandParent = (View) view.getParent();  
  17.                         grandParent.mPrivateFlags |= INVALIDATED;  
  18.                         grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;  
  19.                     }  
  20.                 }  
  21.                 if (drawAnimation) {  
  22.                     if (view != null) {  
  23.                         view.mPrivateFlags |= DRAW_ANIMATION;  
  24.                     } else if (parent instanceof ViewRootImpl) {  
  25.                         ((ViewRootImpl) parent).mIsAnimating = true;  
  26.                     }  
  27.                 }  
  28.                 if (view != null) {  
  29.                     if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&  
  30.                             view.getSolidColor() == 0) {  
  31.                         opaqueFlag = DIRTY;  
  32.                     }  
  33.                     if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {  
  34.                         view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;  
  35.                     }  
  36.                 }  
  37.                 parent = parent.invalidateChildInParent(location, dirty);  
  38.                 if (view != null) {  
  39.                     Matrix m = view.getMatrix();  
  40.                     if (!m.isIdentity()) {  
  41.                         RectF boundingRect = attachInfo.mTmpTransformRect;  
  42.                         boundingRect.set(dirty);  
  43.                         m.mapRect(boundingRect);  
  44.                         dirty.set((int) boundingRect.left, (int) boundingRect.top,  
  45.                                 (int) (boundingRect.right + 0.5f),  
  46.                                 (int) (boundingRect.bottom + 0.5f));  
  47.                     }  
  48.                 }  
  49.             } while (parent != null);  
  50.         }  
  51.     }  
  52. }  

invalidateChildInParent

  1. public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {  
  2.     invalidateChild(null, dirty);  
  3.     return null;  
  4. }  

invalidateChild

  1. public void invalidateChild(View child, Rect dirty) {  
  2.     checkThread();  
  3.     if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);  
  4.     mDirty.union(dirty);  
  5.     if (!mWillDrawSoon) {  
  6.         scheduleTraversals();  
  7.     }  
  8. }  

scheduleTraversals

  1. public void scheduleTraversals() {  
  2.     if (!mTraversalScheduled) {  
  3.         mTraversalScheduled = true;  
  4.         sendEmptyMessage(DO_TRAVERSAL);  
  5.     }  
  6. }  

ViewRoot.handleMessage

  1. public void handleMessage(Message msg) {  
  2.     switch (msg.what) {  
  3.     case DO_TRAVERSAL:  
  4.         if (mProfile) {  
  5.             Debug.startMethodTracing("ViewRoot");  
  6.         }  
  7.         performTraversals();//进行重新绘制  
  8.         if (mProfile) {  
  9.             Debug.stopMethodTracing();  
  10.             mProfile = false;  
  11.         }  
  12.         break;  
  13.     ......  
  14. }  

回头看selectDrawable

  1. public boolean selectDrawable(int idx) {  
  2.     if (idx == mCurIndex) {  
  3.         return false;  
  4.     }  
  5.     final long now = SystemClock.uptimeMillis();  
  6.     if (mDrawableContainerState.mExitFadeDuration > 0) {  
  7.         if (mLastDrawable != null) {  
  8.             mLastDrawable.setVisible(falsefalse);  
  9.         }  
  10.         if (mCurrDrawable != null) {  
  11.             mLastDrawable = mCurrDrawable;  
  12.             mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration;  
  13.         } else {  
  14.             mLastDrawable = null;  
  15.             mExitAnimationEnd = 0;  
  16.         }  
  17.     } else if (mCurrDrawable != null) {  
  18.         mCurrDrawable.setVisible(falsefalse);  
  19.     }  
  20.     if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {  
  21.         Drawable d = mDrawableContainerState.mDrawables[idx];  
  22.         mCurrDrawable = d;  
  23.         mCurIndex = idx;  
  24.         if (d != null) {  
  25.             if (mDrawableContainerState.mEnterFadeDuration > 0) {  
  26.                 mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;  
  27.             } else {  
  28.                 d.setAlpha(mAlpha);  
  29.             }  
  30.             d.setVisible(isVisible(), true);  
  31.             d.setDither(mDrawableContainerState.mDither);  
  32.             d.setColorFilter(mColorFilter);  
  33.             d.setState(getState());  
  34.             d.setLevel(getLevel());  
  35.             d.setBounds(getBounds());  
  36.         }  
  37.     } else {  
  38.         mCurrDrawable = null;  
  39.         mCurIndex = -1;  
  40.     }  
  41.     if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) {  
  42.         if (mAnimationRunnable == null) {  
  43.             mAnimationRunnable = new Runnable() {  
  44.                 @Override public void run() {  
  45.                     animate(true);  
  46.                     invalidateSelf();  
  47.                 }  
  48.             };  
  49.         } else {  
  50.             unscheduleSelf(mAnimationRunnable);  
  51.         }  
  52.         animate(true);  
  53.     }  
  54.     invalidateSelf();//关键  
  55.     return true;  
  56. }  

invalidateSelf

  1. public void invalidateSelf() {  
  2.     final Callback callback = getCallback();  
  3.     if (callback != null) {  
  4.         callback.invalidateDrawable(this);//callback就是view,他自身  
  5.     }  
  6. }  

invalidate()方法虽然最终会调用到performTraversals()方法中,但这时measure和layout流程是不会重新执行的,因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。

而如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了

查看评论
相关频道:

用户评论