闲谈自定义控件源码-view 测量

measure 调用onMeasure(),因为measure的到的宽高会用MeasureSpec封装,先看MeasureSpec
 
    
  1. private static final int MODE_SHIFT = 30;
  2. private static final int MODE_MASK = 0x3 << MODE_SHIFT;
 
     
  1. /**
  2. * Measure specification mode: The parent has not imposed any constraint
  3. * on the child. It can be whatever size it wants.
  4. */
  5. public static final int UNSPECIFIED = 0 << MODE_SHIFT;
  6. /**
  7. * Measure specification mode: The parent has determined an exact size
  8. * for the child. The child is going to be given those bounds regardless
  9. * of how big it wants to be.
  10. */
  11. public static final int EXACTLY = 1 << MODE_SHIFT;
  12. /**
  13. * Measure specification mode: The child can be as large as it wants up
  14. * to the specified size.
  15. */
  16. public static final int AT_MOST = 2 << MODE_SHIFT;
MeasureSpec包含三种模式(mode)
   UNSPECIFIED:父控件未约束,也就是子控件大小随意,比如listview,scrollview一般view用不到
   EXACTLY:当宽或高为固定值或match_parent时,子控件约束值是固定的,mode为exactly
   AT_MOST:当宽或高为wrap_content时,我觉得当viewgroup有两个控件时 一个width 60dp,一个width 70dp,那么
 
      
  1. public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
  2. @MeasureSpecMode int mode) {
  3. if (sUseBrokenMakeMeasureSpec) {
  4. return size + mode;
  5. } else {
  6. return (size & ~MODE_MASK) | (mode & MODE_MASK);
  7. }
  8. }
知道1:mode_mask 为11000.... 所以~mode_mask 为001111...
        2:一个二进制数&1的结果不变,一个二进制数&0的结果都是0
        3:一个二进制数 | 1都得1,一个二进制 | 0不变。
110000 | 001100 = 111100 可见 如果将一个二进制数划分为多个部分。处特定含义部分为0 ,| 运算就想当与加法
110000&000000 = 000000 可见&运算起到一个清0的作用
  ( size & ~ MODE_MASK ) | ( mode & MODE_MASK ); 和  size + mode 的作用是一样的只是位运算的算法效率更高

 
      
  1. public static int getMode(int measureSpec) {
  2. //noinspection ResourceType
  3. return (measureSpec & MODE_MASK);
  4. }
  5. ...
  6. public static int getSize(int measureSpec) {
  7. return (measureSpec & ~MODE_MASK);
  8. }
这两个方法通过位的与运算得到Mode和Size
 
      
  1. /**
  2. * Utility to return a default size. Uses the supplied size if the
  3. * MeasureSpec imposed no constraints. Will get larger if allowed
  4. * by the MeasureSpec.
  5. *
  6. * @param size Default size for this view
  7. * @param measureSpec Constraints imposed by the parent
  8. * @return The size this view should be.
  9. */
  10. public static int getDefaultSize(int size, int measureSpec) {
  11. int result = size;
  12. int specMode = MeasureSpec.getMode(measureSpec);
  13. int specSize = MeasureSpec.getSize(measureSpec);
  14. switch (specMode) {
  15. case MeasureSpec.UNSPECIFIED:
  16. result = size;
  17. break;
  18. case MeasureSpec.AT_MOST:
  19. case MeasureSpec.EXACTLY:
  20. result = specSize;
  21. break;
  22. }
  23. return result;
  24. }
第一个参数size 代表当 Mode为UNSPECIFIED是取的值

    前2位代表mode,后30位代表size

    进入View的onMeasure():
 
     
  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  2. setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
  3. getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
  4. }
先看 setMeasuredDimension () 方法
 
      
  1. /**
  2. *

    This method must be called by {@link #onMeasure(int, int)} to store the

  3. * measured width and measured height. Failing to do so will trigger an
  4. * exception at measurement time.

  5. *
  6. * @param measuredWidth The measured width of this view. May be a complex
  7. * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
  8. * {@link #MEASURED_STATE_TOO_SMALL}.
  9. * @param measuredHeight The measured height of this view. May be a complex
  10. * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
  11. * {@link #MEASURED_STATE_TOO_SMALL}.
  12. */
  13. protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
  14. ...
  15. setMeasuredDimensionRaw(measuredWidth, measuredHeight);
  16. }
这个方法必须被onMeasure()调用,用来保存测量的宽高,否则在执行测量时会触发异常。measureWidth,measureHeight两个参数是MeasureSpec.getSize()的值。

应用试列:测量viewpager每一页的高度,使viewpager每个页面根据控件高度自适应
    
 
    
  1. import android.content.Context;
  2. import android.support.v4.view.ViewPager;
  3. import android.util.AttributeSet;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.view.View;
  7. import com.vcredit.vmoney.utils.CommonUtils;
  8. import com.vcredit.vmoney.utils.ConstantUtils;
  9. /**
  10. * Created by qiubangbang on 2016/8/16.
  11. */
  12. public class WrapContentViewpager extends ViewPager {
  13. // private static final String TAG = "qbb_MyButton";
  14. private int[] pageHeigeht = {0, 0, 0};
  15. public WrapContentViewpager(Context context) {
  16. super(context);
  17. }
  18. public WrapContentViewpager(Context context, AttributeSet attrs) {
  19. super(context, attrs);
  20. }
  21. @Override
  22. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  23. CommonUtils.LOG_D(getClass(), "inestmentpages: onMeasure");
  24. CommonUtils.LOG_D(getClass(), "inestmentpages: onMeasure_item: " + getCurrentItem());
  25. CommonUtils.LOG_D(getClass(), "inestmentpages: getchildCount: " + getChildCount());
  26. // for (int i = 0; i < getChildCount(); i++) {
  27. // View child = getChildAt(i);
  28. // child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
  29. // int h = child.getMeasuredHeight();
  30. // if (h > height) {
  31. // height = h;
  32. // }
  33. // }
  34. View child;
  35. if (getChildCount() > 0) {
  36. //测量展示的viewpager页的高度
  37. //只有pageheight没有值时采取测量
  38. if (pageHeigeht[0] == 0 || pageHeigeht[1] == 0 || pageHeigeht[2] == 0) {
  39. //点击时出现问题,如果不是按部就班滑动,而是直接点击第三页
  40. if (pageHeigeht[1] == 0 && getCurrentItem() == 2) {
  41. child = getChildAt(1);
  42. } else if (pageHeigeht[2] != 0 && getCurrentItem() == 1) {
  43. child = getChildAt(0);
  44. } else {
  45. if (getChildCount() == 2 && getCurrentItem() == 2) {
  46. child = getChildAt(1);
  47. } else {
  48. child = getChildAt(getCurrentItem());
  49. }
  50. }
  51. child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
  52. if (pageHeigeht[getCurrentItem()] == 0) {
  53. pageHeigeht[getCurrentItem()] = child.getMeasuredHeight();
  54. }
  55. }
  56. heightMeasureSpec = MeasureSpec.makeMeasureSpec(pageHeigeht[getCurrentItem()], MeasureSpec.EXACTLY);
  57. }
  58. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  59. }
  60. }


    

你可能感兴趣的:(控件)