Android视图重绘

xiaoxiao2021-02-28  65

学习自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(thisnull);                  return;              }          }          if (p != null && ai != null) {              final Rect r = ai.mTmpInvalRect;              r.set(00, 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(falsefalse);          }          if (mCurrDrawable != null) {              mLastDrawable = mCurrDrawable;              mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration;          } else {              mLastDrawable = null;              mExitAnimationEnd = 0;          }      } else if (mCurrDrawable != null) {          mCurrDrawable.setVisible(falsefalse);      }      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()了

转载请注明原文地址: https://www.6miu.com/read-2626789.html

最新回复(0)