• Android View绘制13问13答


    1.View的绘制流程分几步,从哪开始?哪个过程结束以后能看到view?

    答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程。draw流程结束以后就可以在屏幕上看到view了。

    2.view的测量宽高和实际宽高有区别吗?

    答:基本上百分之99的情况下都是可以认为没有区别的。有两种情况,有区别。第一种 就是有的时候会因为某些原因 view会多次测量,那第一次测量的宽高 肯定和最后实际的宽高 是不一定相等的,但是在这种情况下

    最后一次测量的宽高和实际宽高是一致的。此外,实际宽高是在layout流程里确定的,我们可以在layout流程里 将实际宽高写死 写成硬编码,这样测量的宽高和实际宽高就肯定不一样了,虽然这么做没有意义 而且也不好。

    3.view的measureSpec 由谁决定?顶级view呢?

    答:由view自己的layoutparams和父容器  一起决定自己的measureSpec。一旦确定了spec,onMeasure中就可以确定view的宽高了。

    顶级view就稍微特殊一点,对于decorView的测量在ViewRootImpl的源码里。

     1  
     2 
     3   //desire的这2个参数就代表屏幕的宽高,
     4   childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
     5   childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
     6   performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
     7 
     8 
     9 
    10   //decorView的measureSpec就是在这里确定的,其实比普通view的measurespec要简单的多
    11   //代码就不分析了 一目了然的东西
    12   private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    13         int measureSpec;
    14         switch (rootDimension) {
    15 
    16         case ViewGroup.LayoutParams.MATCH_PARENT:
    17             // Window can't resize. Force root view to be windowSize.
    18             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
    19             break;
    20         case ViewGroup.LayoutParams.WRAP_CONTENT:
    21             // Window can resize. Set max size for root view.
    22             measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
    23             break;
    24         default:
    25             // Window wants to be an exact size. Force root view to be that size.
    26             measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
    27             break;
    28         }
    29         return measureSpec;
    30 }
    View Code

    4.对于普通view来说,他的measure过程中,与父view有关吗?如果有关,这个父view也就是viewgroup扮演了什么角色?

    答:看源码:

      1 //对于普通view的measure来说 是由这个view的 父view ,也就是viewgroup来触发的。
      2 //也就是下面这个measureChildWithMargins方法
      3 
      4 protected void measureChildWithMargins(View child,
      5             int parentWidthMeasureSpec, int widthUsed,
      6             int parentHeightMeasureSpec, int heightUsed) {
      7          //第一步 先取得子view的 layoutParams 参数值   
      8         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
      9 
     10         //然后开始计算子view的spec的值,注意这里看到 计算的时候除了要用子view的 layoutparams参数以外
     11         //还用到了父view 也就是viewgroup自己的spec的值
     12         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
     13                 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
     14                         + widthUsed, lp.width);
     15         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
     16                 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
     17                         + heightUsed, lp.height);
     18 
     19         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
     20 }
     21 
     22 
     23 
     24 
     25 
     26 
     27 //这个算view的spec的方法 看上去一大串 但是真的逻辑非常简单 就是根据父亲viewgroup
     28 //的meaurespec 同时还有view自己的params来确定 view自己的measureSpec。
     29 //注意这里的参数是padding,这个值的含义是 父容器已占用的控件的大小 所以view的Specsize
     30 //的值 你们可以看到 是要减去这个padding的值的。总大小-已经用的 =可用的。 很好理解。
     31 
     32 //然后就是下面的switch逻辑 要自己梳理清楚。其实也不难,主要是下面几条原则
     33 //如果view采用固定宽高,也就是写死的数值那种。那就不管父亲的spec的值了,view的spec 就肯定是exactly 并且大小遵循layout参数里设置的大小。
     34 
     35 //如果view的宽高是match_parent ,那么就要看父容器viewgroup的 spec的值了,如果父view的spec是exactly模式,
     36 //那view也肯定是exactly,并且大小就是父容器剩下的空间。如果父容器是at_most模式,那view也是at_most 并且不会超过剩余空间大小
     37 
     38 //如果view的宽高是wrap_content, 那就不管父容器的spec了,view的spec一定是at_most 并且不会超过父view 剩余空间的大小。
     39 
     40 
     41 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
     42         int specMode = MeasureSpec.getMode(spec);
     43         int specSize = MeasureSpec.getSize(spec);
     44 
     45         int size = Math.max(0, specSize - padding);
     46 
     47         int resultSize = 0;
     48         int resultMode = 0;
     49 
     50         switch (specMode) {
     51         // Parent has imposed an exact size on us
     52         case MeasureSpec.EXACTLY:
     53             if (childDimension >= 0) {
     54                 resultSize = childDimension;
     55                 resultMode = MeasureSpec.EXACTLY;
     56             } else if (childDimension == LayoutParams.MATCH_PARENT) {
     57                 // Child wants to be our size. So be it.
     58                 resultSize = size;
     59                 resultMode = MeasureSpec.EXACTLY;
     60             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
     61                 // Child wants to determine its own size. It can't be
     62                 // bigger than us.
     63                 resultSize = size;
     64                 resultMode = MeasureSpec.AT_MOST;
     65             }
     66             break;
     67 
     68         // Parent has imposed a maximum size on us
     69         case MeasureSpec.AT_MOST:
     70             if (childDimension >= 0) {
     71                 // Child wants a specific size... so be it
     72                 resultSize = childDimension;
     73                 resultMode = MeasureSpec.EXACTLY;
     74             } else if (childDimension == LayoutParams.MATCH_PARENT) {
     75                 // Child wants to be our size, but our size is not fixed.
     76                 // Constrain child to not be bigger than us.
     77                 resultSize = size;
     78                 resultMode = MeasureSpec.AT_MOST;
     79             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
     80                 // Child wants to determine its own size. It can't be
     81                 // bigger than us.
     82                 resultSize = size;
     83                 resultMode = MeasureSpec.AT_MOST;
     84             }
     85             break;
     86 
     87         // Parent asked to see how big we want to be
     88         case MeasureSpec.UNSPECIFIED:
     89             if (childDimension >= 0) {
     90                 // Child wants a specific size... let him have it
     91                 resultSize = childDimension;
     92                 resultMode = MeasureSpec.EXACTLY;
     93             } else if (childDimension == LayoutParams.MATCH_PARENT) {
     94                 // Child wants to be our size... find out how big it should
     95                 // be
     96                 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
     97                 resultMode = MeasureSpec.UNSPECIFIED;
     98             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
     99                 // Child wants to determine its own size.... find out how
    100                 // big it should be
    101                 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
    102                 resultMode = MeasureSpec.UNSPECIFIED;
    103             }
    104             break;
    105         }
    106         return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    107     }
    View Code

    5.view的meaure和onMeasure有什么关系?

    答:看源码:

     1 //view的measure是final 方法 我们子类无法修改的。
     2  public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
     3         boolean optical = isLayoutModeOptical(this);
     4         if (optical != isLayoutModeOptical(mParent)) {
     5             Insets insets = getOpticalInsets();
     6             int oWidth  = insets.left + insets.right;
     7             int oHeight = insets.top  + insets.bottom;
     8             widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
     9             heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
    10         }
    11 
    12         // Suppress sign extension for the low bytes
    13         long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
    14         if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
    15 
    16         if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
    17                 widthMeasureSpec != mOldWidthMeasureSpec ||
    18                 heightMeasureSpec != mOldHeightMeasureSpec) {
    19 
    20             // first clears the measured dimension flag
    21             mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
    22 
    23             resolveRtlPropertiesIfNeeded();
    24 
    25             int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
    26                     mMeasureCache.indexOfKey(key);
    27             if (cacheIndex < 0 || sIgnoreMeasureCache) {
    28                 // measure ourselves, this should set the measured dimension flag back
    29                 onMeasure(widthMeasureSpec, heightMeasureSpec);
    30                 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    31             } else {
    32                 long value = mMeasureCache.valueAt(cacheIndex);
    33                 // Casting a long to int drops the high 32 bits, no mask needed
    34                 setMeasuredDimensionRaw((int) (value >> 32), (int) value);
    35                 mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    36             }
    37 
    38             // flag not set, setMeasuredDimension() was not invoked, we raise
    39             // an exception to warn the developer
    40             if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
    41                 throw new IllegalStateException("View with id " + getId() + ": "
    42                         + getClass().getName() + "#onMeasure() did not set the"
    43                         + " measured dimension by calling"
    44                         + " setMeasuredDimension()");
    45             }
    46 
    47             mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
    48         }
    49 
    50         mOldWidthMeasureSpec = widthMeasureSpec;
    51         mOldHeightMeasureSpec = heightMeasureSpec;
    52 
    53         mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
    54                 (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
    55     }
    56 
    57 //不过可以看到的是在measure方法里调用了onMeasure方法
    58 //所以就能知道 我们在自定义view的时候一定是重写这个方法!
    59  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    60         setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
    61                 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    62     }
    View Code

    6.简要分析view的measure流程?

    答:先回顾问题4,viewgroup 算出子view的spec以后 会调用子view的measure方法,而子view的measure方法 我们问题5也看过了实际上是调用的onMeasure方法

    所以我们只要分析好onMeasure方法即可,注意onMeasure方法的参数 正是他的父view算出来的那2个spec的值(这里view的measure方法会把这个spec里的specSize值做略微的修改 这个部分 不做分析 因为measure方法修改specSize的部分很简单)。

     1  //可以看出来这个就是setMeasuredDimension方法的调用 这个方法看名字就知道就是确定view的测量宽高的
     2 //所以我们分析的重点就是看这个getDefaultSize 方法 是怎么确定view的测量宽高的
     3  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     4         setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
     5                 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
     6     }
     7 
     8 
     9 //这个方法特别简单 基本可以认为就是近似的返回spec中的specSize,除非你的specMode是UNSPECIFIED
    10 //UNSPECIFIED 这个一般都是系统内部测量才用的到,这种时候返回size 也就是getSuggestedMinimumWidth的返回值
    11  public static int getDefaultSize(int size, int measureSpec) {
    12         int result = size;
    13         int specMode = MeasureSpec.getMode(measureSpec);
    14         int specSize = MeasureSpec.getSize(measureSpec);
    15 
    16         switch (specMode) {
    17         case MeasureSpec.UNSPECIFIED:
    18             result = size;
    19             break;
    20         case MeasureSpec.AT_MOST:
    21         case MeasureSpec.EXACTLY:
    22             result = specSize;
    23             break;
    24         }
    25         return result;
    26 }
    27 
    28 //跟view的背景相关 这里不多做分析了
    29 protected int getSuggestedMinimumWidth() {
    30         return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    31     }
    View Code

    7.自定义view中 如果onMeasure方法 没有对wrap_content 做处理 会发生什么?为什么?怎么解决?

    答:如果没有对wrap_content做处理 ,那即使你在xml里设置为wrap_content.其效果也和match_parent相同。看问题4的分析。我们可以知道view自己的layout为wrap,那mode就是at_most(不管父亲view是什么specmode).

    这种模式下宽高就是等于specSize(getDefaultSize函数分析可知),而这里的specSize显然就是parentSize的大小。也就是父容器剩余的大小。那不就和我们直接设置成match_parent是一样的效果了么?

    解决方式就是在onMeasure里 针对wrap 来做特殊处理 比如指定一个默认的宽高,当发现是wrap_content 就设置这个默认宽高即可。

    8.ViewGroup有onMeasure方法吗?为什么?

    答:没有,这个方法是交给子类自己实现的。不同的viewgroup子类 肯定布局都不一样,那onMeasure索性就全部交给他们自己实现好了。

    9.为什么在activity的生命周期里无法获得测量宽高?有什么方法可以解决这个问题吗?

    答:因为measure的过程和activity的生命周期  没有任何关系。你无法确定在哪个生命周期执行完毕以后 view的measure过程一定走完。可以尝试如下几种方法 获取view的测量宽高。

     1 //重写activity的这个方法
     2 public void onWindowFocusChanged(boolean hasFocus) {
     3         super.onWindowFocusChanged(hasFocus);
     4         if (hasFocus) {
     5             int width = tv.getMeasuredWidth();
     6             int height = tv.getMeasuredHeight();
     7             Log.v("burning", "width==" + width);
     8             Log.v("burning", "height==" + height);
     9 
    10         }
    11     }
    View Code

    或者重写这个方法

     1  @Override
     2     protected void onStart() {
     3         super.onStart();
     4         tv.post(new Runnable() {
     5             @Override
     6             public void run() {
     7                 int width = tv.getMeasuredWidth();
     8                 int height = tv.getMeasuredHeight();
     9             }
    10         });
    11     }
    View Code

    再或者:

     1  @Override
     2     protected void onStart() {
     3         super.onStart();
     4         ViewTreeObserver observer = tv.getViewTreeObserver();
     5         observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
     6             @Override
     7             public void onGlobalLayout() {
     8                 int width = tv.getMeasuredWidth();
     9                 int height = tv.getMeasuredHeight();
    10                 tv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    11             }
    12         });
    13     }
    View Code

    10.layout和onLayout方法有什么区别?

    答:layout是确定本身view的位置 而onLayout是确定所有子元素的位置。layout里面 就是通过serFrame方法设设定本身view的 四个顶点的位置。这4个位置以确定 自己view的位置就固定了

    然后就调用onLayout来确定子元素的位置。view和viewgroup的onlayout方法都没有写。都留给我们自己给子元素布局

    11.draw方法 大概有几个步骤?

    答: 一共是4个步骤, 绘制背景---------绘制自己--------绘制chrildren----绘制装饰。

    12.setWillNotDraw方法有什么用?

    答:这个方法在view里。

     1  /**
     2      * If this view doesn't do any drawing on its own, set this flag to
     3      * allow further optimizations. By default, this flag is not set on
     4      * View, but could be set on some View subclasses such as ViewGroup.
     5      *
     6      * Typically, if you override {@link #onDraw(android.graphics.Canvas)}
     7      * you should clear this flag.
     8      *
     9      * @param willNotDraw whether or not this View draw on its own
    10      */
    11     public void setWillNotDraw(boolean willNotDraw) {
    12         setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
    13     }
    View Code

    用于设置标志位的 也就是说 如果你的自定义view 不需要draw的话,就可以设置这个方法为true。这样系统知道你这个view 不需要draw 可以优化执行速度。viewgroup 一般都默认设置这个为true,因为viewgroup多数都是只负责布局

    不负责draw的。而view 这个标志位 默认一般都是关闭的。

    13.自定义view 有哪些需要注意的点?

    答:主要是要处理wrap_content 和padding。否则xml 那边设置这2个属性就根本没用了。还有不要在view中使用handler 因为人家已经提供了post方法。如果是继承自viewGroup,那在onMeasure和onLayout里面 也要考虑

    padding和layout的影响。也就是说specSize 要算一下 。最后就是如果view的动画或者线程需要停止,可以考虑在onDetachedFromWindow里面来做。

    针对上述的几点,给出几个简单的自定义view 供大家理解。

    给出一个圆形的view 范例:

     1 package com.example.administrator.motioneventtest;
     2 
     3 import android.content.Context;
     4 import android.graphics.Canvas;
     5 import android.graphics.Color;
     6 import android.graphics.Paint;
     7 import android.util.AttributeSet;
     8 import android.view.View;
     9 
    10 /**
    11  * Created by Administrator on 2016/2/4.
    12  */
    13 public class CircleView extends View {
    14 
    15     private int mColor = Color.RED;
    16     private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    17 
    18     private void init() {
    19         mPaint.setColor(mColor);
    20     }
    21 
    22     @Override
    23     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    24         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    25         int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    26         int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    27         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    28         int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    29 
    30         //处理为wrap_content时的情况
    31         if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
    32             setMeasuredDimension(200, 200);
    33         } else if (widthSpecMode == MeasureSpec.AT_MOST) {
    34             setMeasuredDimension(200, heightSpecSize);
    35         } else if (heightSpecMode == MeasureSpec.AT_MOST) {
    36             setMeasuredDimension(widthSpecSize, 200);
    37         }
    38 
    39     }
    40 
    41     @Override
    42     protected void onDraw(Canvas canvas) {
    43         super.onDraw(canvas);
    44         //处理padding的情况
    45         final int paddingLeft = getPaddingLeft();
    46         final int paddingRight = getPaddingRight();
    47         final int paddingTop = getPaddingTop();
    48         final int paddingBottom = getPaddingBottom();
    49 
    50 
    51         int width = getWidth() - paddingLeft - paddingRight;
    52         int height = getHeight() - paddingTop - paddingBottom;
    53         int radius = Math.min(width, height) / 2;
    54         canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2, radius, mPaint);
    55     }
    56 
    57     public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
    58         super(context, attrs, defStyleAttr);
    59         init();
    60     }
    61 
    62     public CircleView(Context context) {
    63         super(context);
    64         init();
    65 
    66     }
    67 
    68     public CircleView(Context context, AttributeSet attrs) {
    69         super(context, attrs);
    70         init();
    71     }
    72 }
    View Code

    然后下面再给出一个范例,稍微复杂一点是自定义viewgroup了(主要是加强对onMeasure和onLayout的理解), 需求如下:

    一个水平的viewgroup,内部的子元素 为了简单 我们假定他们的宽高都是一样的。来写一个这样的简单的viewgroup。

     1 package com.example.administrator.motioneventtest;
     2 
     3 import android.content.Context;
     4 import android.util.AttributeSet;
     5 import android.util.Log;
     6 import android.view.View;
     7 import android.view.ViewGroup;
     8 
     9 /**
    10  * Created by Administrator on 2016/2/4.
    11  */
    12 //这里我们只处理了padding的状态 没有处理margin的状态,子view的margin 对measure和layout的影响
    13 //就留给读者自己完成了
    14 public class CustomHorizontalLayout extends ViewGroup {
    15 
    16     //设置默认的控件最小是多少 这里不提供自定义属性了 写死在代码里 你们可以自行拓展
    17     final int minHeight = 0;
    18     final int minWidth = 0;
    19 
    20 
    21     public CustomHorizontalLayout(Context context) {
    22         super(context);
    23     }
    24 
    25     public CustomHorizontalLayout(Context context, AttributeSet attrs) {
    26         super(context, attrs);
    27     }
    28 
    29     public CustomHorizontalLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    30         super(context, attrs, defStyleAttr);
    31     }
    32 
    33     @Override
    34     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    35         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    36         int measureWidth = 0;
    37         int measureHeight = 0;
    38         final int childCount = getChildCount();
    39         measureChildren(widthMeasureSpec, heightMeasureSpec);
    40         int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    41         int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    42         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    43         int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    44         final View childView = getChildAt(0);
    45         final int paddingLeft = getPaddingLeft();
    46         final int paddingRight = getPaddingRight();
    47         final int paddingTop = getPaddingTop();
    48         final int paddingBottom = getPaddingBottom();
    49         //没有子控件 时 我们的宽高要作特殊处理
    50         if (childCount == 0) {
    51             //当没有子控件时,如果长宽有一个为wrap 那么就让这个控件以最小的形式展现
    52             //这里我们最小设置为0
    53             if (widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {
    54                 setMeasuredDimension(minWidth, minHeight);
    55             } else {
    56                 //否则根据我们的layout属性来
    57                 setMeasuredDimension(getLayoutParams().width, getLayoutParams().height);
    58             }
    59 
    60         } else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
    61             measureWidth = childView.getMeasuredWidth() * childCount;
    62             measureHeight = childView.getMeasuredHeight();
    63             setMeasuredDimension(paddingLeft + measureWidth + paddingRight, paddingTop + measureHeight + paddingBottom);
    64         } else if (heightSpecMode == MeasureSpec.AT_MOST) {
    65             measureHeight = childView.getMeasuredHeight();
    66             setMeasuredDimension(paddingLeft + paddingRight + widthSpecSize, paddingTop + paddingBottom + measureHeight);
    67         } else if (widthSpecMode == MeasureSpec.AT_MOST) {
    68             measureWidth = childView.getMeasuredWidth() * childCount;
    69             setMeasuredDimension(paddingLeft + paddingRight + measureWidth, paddingTop + paddingBottom + heightSpecSize);
    70         }
    71     }
    72 
    73     @Override
    74     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    75         final int paddingLeft = getPaddingLeft();
    76         final int paddingRight = getPaddingRight();
    77         final int paddingTop = getPaddingTop();
    78         final int paddingBottom = getPaddingBottom();
    79         //左边初始位置为0
    80         int childLeft = 0 + paddingLeft;
    81         final int childCount = getChildCount();
    82         for (int i = 0; i < childCount; i++) {
    83             final View childView = getChildAt(i);
    84             if (childView.getVisibility() != View.GONE) {
    85                 final int childWidth = childView.getMeasuredWidth();
    86                 childView.layout(childLeft, 0 + paddingTop, childLeft + childWidth, paddingTop + childView.getMeasuredHeight());
    87                 childLeft += childWidth;
    88             }
    89         }
    90     }
    91 }
    View Code
  • 相关阅读:
    Android中连接蓝牙设备时遇到createRfcommSocketToServiceRecord的UUID问题和BluetoothSocket的connect失败
    android4.0蓝牙使能的详细解析 (转载)
    蓝牙介绍
    Bluetooth 4.0之Android 讲解
    jQuery来源学习笔记:扩展的实用功能
    Linux 0.12 内核管理存储器
    java战斗系列-战斗MAVENPW结构
    牟大哥:《App自我促销》连载2 直立人迁移走
    AC自己主动机 总结
    SpringMVC 上下文webApplicationContext
  • 原文地址:https://www.cnblogs.com/punkisnotdead/p/5181821.html
Copyright © 2020-2023  润新知