android自定义控件(一)MeasureSpec 与 ListView.onMeasure

A MeasureSpec encapsulates the layout requirements passed from parent to child. Each MeasureSpec represents a requirement

for either the width or the height. A MeasureSpec is comprised of a size and a mode. There are three possible modes:

UNSPECIFIED
  The parent has not imposed any constraint on the child. It can be whatever size it wants.
EXACTLY
  The parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be.
AT_MOST
  The child can be as large as it wants up to the specified size.

1.使用 View.resolveSize(int size,int measureSpec)

public static int resolveSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize =  MeasureSpec.getSize(measureSpec);
        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
            result = Math.min(size, specSize);
            break;
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

 

 

2.源码

    public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        public static final int EXACTLY     = 1 << MODE_SHIFT;

        public static final int AT_MOST     = 2 << MODE_SHIFT;

        

        public static int  makeMeasureSpec(int size, int mode) {
            return size + mode;
        }


        public static int  getMode(int measureSpec) {
            return (measureSpec & MODE_MASK);
        }

        public static int  getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }


    }

示例:

int measureSpec = makeMeasureSpec(4, EXACTLY);
getSize(measureSpec);
getMode(measureSpec);

 

-----------------------makeMeasureSpec --------------------- mode+size

mode:            1000  0000  0000  0000  0000  0000  0000  000
size:                                       100
measureSpec: 1000  0000  0000  0000  0000  0000  0000  100


---------------getMode  ---------------- MODE_MASK & measureSpec

MODE_MASK:  1100  0000  0000  0000  0000  0000  0000  0000
measureSpec:  1000  0000  0000  0000  0000  0000  0000  100

  

3.示例ListView.measureItem(View child)

private void measureItem(View child) {
        ViewGroup.LayoutParams p = child.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }

        int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
                mListPadding.left + mListPadding.right, p.width);
        int lpHeight = p.height;
        int childHeightSpec;
        if (lpHeight > 0) {
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }
        child.measure(childWidthSpec, childHeightSpec);
    }

 

 4.重写ListView与ScrollView 兼容:

 

protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)

Parameters

  • widthMeasureSpec horizontal space requirements as imposed by the parent. The requirements are encoded with View.MeasureSpec.
  • heightMeasureSpec vertical space requirements as imposed by the parent. The requirements are encoded with View.MeasureSpec.

 重写:

public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
  super.onMeasure(widthMeasureSpec, expandSpec);
 
}

 ListView

   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Sets up mListPadding
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int childWidth = 0;
        int childHeight = 0;

        mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
        if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED ||
                heightMode == MeasureSpec.UNSPECIFIED)) {
            final View child = obtainView(0);

            measureScrapChild(child, 0, widthMeasureSpec);

            childWidth = child.getMeasuredWidth();
            childHeight = child.getMeasuredHeight();

            if (recycleOnMeasure()) {
                mRecycler.addScrapView(child);
            }
        }

        if (widthMode == MeasureSpec.UNSPECIFIED) {
            widthSize = mListPadding.left + mListPadding.right + childWidth +
                    getVerticalScrollbarWidth();
        }

        if (heightMode == MeasureSpec.UNSPECIFIED) {
            heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                    getVerticalFadingEdgeLength() * 2;
        }

        if (heightMode == MeasureSpec.AT_MOST) {
            // TODO: after first layout we should maybe start at the first visible position, not 0
            heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
        }

        setMeasuredDimension(widthSize, heightSize);
        mWidthMeasureSpec = widthMeasureSpec;        
    }

 

 

posted @ 2014-05-18 18:28  等风来。。  Views(1297)  Comments(0Edit  收藏  举报
------------------------------------------------------------------------------------------------------------ --------------- 欢迎联系 x.guan.ling@gmail.com--------------- ------------------------------------------------------------------------------------------------------------