美文网首页Android开发经验谈Android知识Android
工作日记第五篇(自定义View<onMeasure和onL

工作日记第五篇(自定义View<onMeasure和onL

作者: We7ex | 来源:发表于2016-08-01 13:32 被阅读116次

    首先声明下面的代码是从http://blog.csdn.net/lmj623565791/article/details/38352503摘取,个人认为这段代码对于理解自定义View有很大的帮助。

    package com.zhy.zhy_flowlayout02;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class FlowLayout extends ViewGroup
    {
    
        private static final String TAG = "FlowLayout";
    
    
        public FlowLayout(Context context, AttributeSet attrs)
        {
            super(context, attrs);
        }
    
        @Override
        protected ViewGroup.LayoutParams generateLayoutParams(
                ViewGroup.LayoutParams p)
        {
            return new MarginLayoutParams(p);
        }
    
        @Override
        public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
        {
            return new MarginLayoutParams(getContext(), attrs);
        }
    
        @Override
        protected ViewGroup.LayoutParams generateDefaultLayoutParams()
        {
            return new MarginLayoutParams(LayoutParams.MATCH_PARENT,
                    LayoutParams.MATCH_PARENT);
        }
    
        /**
         * 负责设置子控件的测量模式和大小 根据所有子控件设置自己的宽和高
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            // 获得它的父容器为它设置的测量模式和大小
            int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
            int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
            int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
            int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
    
            Log.e(TAG, sizeWidth + "," + sizeHeight);
    
            // 如果是warp_content情况下,记录宽和高
            int width = 0;
            int height = 0;
            /**
             * 记录每一行的宽度,width不断取最大宽度
             */
            int lineWidth = 0;
            /**
             * 每一行的高度,累加至height
             */
            int lineHeight = 0;
    
            int cCount = getChildCount();
    
            // 遍历每个子元素
            for (int i = 0; i < cCount; i++)
            {
                View child = getChildAt(i);
                // 测量每一个child的宽和高
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
                // 得到child的lp
                MarginLayoutParams lp = (MarginLayoutParams) child
                        .getLayoutParams();
                // 当前子空间实际占据的宽度
                int childWidth = child.getMeasuredWidth() + lp.leftMargin
                        + lp.rightMargin;
                // 当前子空间实际占据的高度
                int childHeight = child.getMeasuredHeight() + lp.topMargin
                        + lp.bottomMargin;
                /**
                 * 如果加入当前child,则超出最大宽度,则的到目前最大宽度给width,类加height 然后开启新行
                 */
                if (lineWidth + childWidth > sizeWidth)
                {
                    width = Math.max(lineWidth, childWidth);// 取最大的
                    lineWidth = childWidth; // 重新开启新行,开始记录
                    // 叠加当前高度,
                    height += lineHeight;
                    // 开启记录下一行的高度
                    lineHeight = childHeight;
                } else
                // 否则累加值lineWidth,lineHeight取最大高度
                {
                    lineWidth += childWidth;
                    lineHeight = Math.max(lineHeight, childHeight);
                }
                // 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较
                if (i == cCount - 1)
                {
                    width = Math.max(width, lineWidth);
                    height += lineHeight;
                }
    
            }
            setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth
                    : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight
                    : height);
    
        }
        /**
         * 存储所有的View,按行记录
         */
        private List<List<View>> mAllViews = new ArrayList<List<View>>();
        /**
         * 记录每一行的最大高度
         */
        private List<Integer> mLineHeight = new ArrayList<Integer>();
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b)
        {
            mAllViews.clear();
            mLineHeight.clear();
    
            int width = getWidth();
    
            int lineWidth = 0;
            int lineHeight = 0;
            // 存储每一行所有的childView
            List<View> lineViews = new ArrayList<View>();
            int cCount = getChildCount();
            // 遍历所有的孩子
            for (int i = 0; i < cCount; i++)
            {
                View child = getChildAt(i);
                MarginLayoutParams lp = (MarginLayoutParams) child
                        .getLayoutParams();
                int childWidth = child.getMeasuredWidth();
                int childHeight = child.getMeasuredHeight();
    
                // 如果已经需要换行
                if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width)
                {
                    // 记录这一行所有的View以及最大高度
                    mLineHeight.add(lineHeight);
                    // 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView
                    mAllViews.add(lineViews);
                    lineWidth = 0;// 重置行宽
                    lineViews = new ArrayList<View>();
                }
                /**
                 * 如果不需要换行,则累加
                 */
                lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
                lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
                        + lp.bottomMargin);
                lineViews.add(child);
            }
            // 记录最后一行
            mLineHeight.add(lineHeight);
            mAllViews.add(lineViews);
    
            int left = 0;
            int top = 0;
            // 得到总行数
            int lineNums = mAllViews.size();
            for (int i = 0; i < lineNums; i++)
            {
                // 每一行的所有的views
                lineViews = mAllViews.get(i);
                // 当前行的最大高度
                lineHeight = mLineHeight.get(i);
    
                Log.e(TAG, "第" + i + "行 :" + lineViews.size() + " , " + lineViews);
                Log.e(TAG, "第" + i + "行, :" + lineHeight);
    
                // 遍历当前行所有的View
                for (int j = 0; j < lineViews.size(); j++)
                {
                    View child = lineViews.get(j);
                    if (child.getVisibility() == View.GONE)
                    {
                        continue;
                    }
                    MarginLayoutParams lp = (MarginLayoutParams) child
                            .getLayoutParams();
    
                    //计算childView的left,top,right,bottom
                    int lc = left + lp.leftMargin;
                    int tc = top + lp.topMargin;
                    int rc =lc + child.getMeasuredWidth();
                    int bc = tc + child.getMeasuredHeight();
    
                    Log.e(TAG, child + " , l = " + lc + " , t = " + t + " , r ="
                            + rc + " , b = " + bc);
    
                    child.layout(lc, tc, rc, bc);
                    
                    left += child.getMeasuredWidth() + lp.rightMargin
                            + lp.leftMargin;
                }
                left = 0;
                top += lineHeight;
            }
    
        }
    }
    
    

    demo截图如下:

    QQ图片20160801132634.jpg

    以下是个人的理解:
    首先,onMeasure(int widthMeasureSpec, int heightMeasureSpec)。这个方法是用于测量该控件以及子控件的大小等属性。widthMeasureSpec、heightMeasureSpec分别包括了宽和高的size和mode。
    1、size:通过MeasureSpec.getSize(widthMeasureSpec)可以获取系统测量后建议宽度,高度同理。
    2、 mode:通过MeasureSpec.getMode(widthMeasureSpec)可以获取系统测量时的测量模式分别为AT_MOST、EXACTLY、UNSPECIFIED。AT_MOST:当子控件属性设置为wrap_content时所使用的测量模式。EXACTLY:当子控件属性指明数值时使用的测量模式<如70dp>。

    上述代码计算过后根据所使用的测量模式mode来判断最后是否使用代码计算的数值来设置该空间的大小

    setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth
                    : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight
                    : height);
    
      onLayout就比较好理解了,根据测量出来的数据,将子控件按照要求排放当相应的位置即可

    相关文章

      网友评论

        本文标题:工作日记第五篇(自定义View<onMeasure和onL

        本文链接:https://www.haomeiwen.com/subject/cuixsttx.html