当前位置:   article > 正文

view的onMeasure,onLayout,onDraw源码分析(下)_relativelayout执行ondraw

relativelayout执行ondraw

1, 引子

一直以为绘制的三个方法onMeasure,onLayout,和onDraw只调用一次,分析源码时也没有在意,后来在看listview时发现onLayout居然调用了2次,然后就有些疑惑了,这些方法到底是调用几次。其实, ViewRootImpl 的performTraversals方法调用2次, View的onMeasure方法至少调用2次, onLayout方法调用2次, onDraw方法调用1次。调用顺序如下,

performTraversals –>onMeasure-->onMeasure-->onLayout

performTraversals –>onMeasure--> onLayout -->onDraw

2, 2次performTraversals

ViewRootImpl 的performTraversals部分源码如下,

  1. private void performTraversals() {
  2. mIsInTraversal = true;
  3. mWillDrawSoon = true;
  4. boolean windowSizeMayChange = false;
  5. boolean newSurface = false;
  6. boolean surfaceChanged = false;
  7. WindowManager.LayoutParams lp = mWindowAttributes;
  8. ~~~
  9. if (!cancelDraw && !newSurface) {
  10. if (!skipDraw || mReportNextDraw) {
  11. if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
  12. for (int i = 0; i < mPendingTransitions.size(); ++i) {
  13. mPendingTransitions.get(i).startChangingAnimations();
  14. }
  15. mPendingTransitions.clear();
  16. }
  17. performDraw();
  18. }
  19. } else {
  20. if (viewVisibility == View.VISIBLE) {
  21. // Try again
  22. scheduleTraversals();
  23. } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
  24. for (int i = 0; i < mPendingTransitions.size(); ++i) {
  25. mPendingTransitions.get(i).endChangingAnimations();
  26. }
  27. mPendingTransitions.clear();
  28. }
  29. }
  30. mIsInTraversal = false;
  31. }

第1次执行performTraversals方法时, newSurface为false,所以会执行scheduleTraversals方法,该方法最后又会调用performTraversals方法,第2次执行performTraversals方法时,newSurface为true,所以会调用performDraw方法,最后会调用onDraw方法。

3, measure

View的measure部分源码如下,

  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  2. ~~~
  3. if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
  4. widthMeasureSpec != mOldWidthMeasureSpec ||
  5. heightMeasureSpec != mOldHeightMeasureSpec) {
  6. ~~~
  7. if (cacheIndex < 0 || sIgnoreMeasureCache) {
  8. // measure ourselves, this should set the measured dimension flag back
  9. onMeasure(widthMeasureSpec, heightMeasureSpec);
  10. mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  11. }
  12. ~~~
  13. }

有些ViewGroup可能会让自己的子视图测量两次。比如说,父视图先让每个子视图自己测量,使用View.MeasureSpec.UNSPECIFIED,然后在根据每个子视图希望得到的大小不超过父视图的一些限制,就让子视图得到自己希望的大小,否则就用其他尺寸来重新测量子视图。这一类的视图有FrameLayout,RelativeLayout等。所以,在measure方法中,对重新调用onMeasure方法有些限制。

所以, measure方法至少会调用2次, onLayout一定会调用2次, onDraw方法仅会调用1次。

4,View状态

View状态的种类非常多,一共有十几种类型,不过多数情况下我们只会使用到其中的几种,因此这里我们也就只去分析最常用的几种视图状态。

enabled

setEnabled

isEnabled

focused

setFocusable

isFocusable

selected

 

 

pressed

 

 

enabled

setEnable()方法来改变视图的可用状态,传入true表示可用,传入false表示不可用。它们之间最大的区别在于,不可用的视图是无法响应onTouch事件的。

focused

表示当前视图是否获得到焦点。通常情况下有两种方法可以让视图获得焦点,即通过键盘的上下左右键切换视图,以及调用requestFocus()方法。

selected

表示当前视图是否处于选中状态。一个界面当中可以有多个视图处于选中状态,调用setSelected()方法能够改变视图的选中状态,传入true表示选中,传入false表示未选中。

pressed

表示当前视图是否处于按下状态。可以调用setPressed()方法来对这一状态进行改变,传入true表示按下,传入false表示未按下。


一般当View的状态发生改变时,会重新绘制View,刷新界面。

当View的状态发生改变时,最后都会调用ViewRootImpl的performTraversals方法进行刷新,具体的细节就不多说了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/673371
推荐阅读
相关标签
  

闽ICP备14008679号