自定义ViewGroup之流式布局

分析布局

  1. 对于FlowLayout,需要指定的LayoutParams,我们目前只需要能够识别margin即可,即使用MarginLayoutParams.
  2. onMeasure中计算所有childView的宽和高,然后根据childView的宽和高,计算自己的宽和高。(当然,如果不是wrap_content,直接使用父ViewGroup传入的计算值即可)
  3. onLayout中对所有的childView进行布局。

效果如下

自定义ViewGroup之流式布局_第1张图片
device-2016-05-15-235345.png

自定义VIewGroup代码

public class FlowLayout extends ViewGroup{

    //存储所有的View,按行记录
    private List> mAllView = new ArrayList>();

    //记录每一行的最大高度
    private List mLineHeight = new ArrayList();

    private String TAG = "TAG";
    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new MarginLayoutParams(p);
    }

    /**
     * 这个一定要设置,否则会包强转错误
     * 设置它支持 marginLayoutParams
     */
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
    }

    //负责设置子控件的测量模式和大小 根据所有子控件设置自己的宽和高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //置空 view 容器 和 lineHeight 容器  重新赋值
        mAllView.clear();
        mLineHeight.clear();
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

        // 计算出所有的 child 的 宽和高
        measureChildren(sizeWidth, sizeHeight);
        //如果是wrap_content情况下,记录宽和高
        int width = 0;
        int height = 0;

        //记录每一行的宽度,width不断取最大宽度
        int lineWidth = 0;
        //每一行的高度,累加至height
        int lineHeight = 0;

        //存储每一行的chileVIew
        List lineViews = new ArrayList();

        int cCount = getChildCount();

        //遍历每个子元素
        for (int i = 0; i < cCount; i++){
            View child = getChildAt(i);

            //测量每一个child的宽和高
            measureChild(child, widthMeasureSpec,heightMeasureSpec);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

            //当前子控件实际占据的宽度
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            //当前子空间实际占据的高度
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            //需要换行
            if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()){
                width = Math.max(lineWidth, childWidth);
                lineWidth = childWidth;   //重新开启新行,开始记录

                height += lineHeight;  //叠加高度
                //开启记录下一行的高度
                lineHeight = childHeight;

                mAllView.add(lineViews);
                //记录这一行所有的View以及最大高度
                mLineHeight.add(lineHeight);
                lineViews = new ArrayList();
                // 这个  view(child) 是下一行的第一个view
                lineViews.add(child);

                //Log.v(TAG, "onl  mAllViews.size()  --  > " + mAllView.size());
            } else {
                //否则累加值lineWidth,lineHeight取最大高度
                lineWidth += childWidth;
                lineHeight = Math.max(lineHeight, childHeight); //去height最大的值
                lineViews.add(child);
            }
            if (i == cCount - 1){
                width = Math.max(width, lineWidth);
                height += lineHeight;  //如果是最后一行,则叠加其高度
            }
        }

        // 循环结束后 把最后一行内容add进集合中
        mLineHeight.add(lineHeight); // 记录最后一行
        mAllView.add(lineViews);
        //计算容器的宽高
        setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth
            :width  + getPaddingLeft() + getPaddingRight(),
                (modeHeight == MeasureSpec.EXACTLY)?sizeHeight:height + getPaddingTop()+ getPaddingBottom());
    }



    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int width = getWidth();

        //当前行的最大值
        int lineWidth = 0;
        int lineHeight = 0;

        //存储每一行所有的ChildView
        List lineViews = new ArrayList();

        //考虑到padding情况,所以开始要加上padding
        int left = getPaddingLeft();
        int top = getPaddingTop();
        //得到总行数
        int lineNums = mAllView.size();
        for (int i = 0; i < lineNums; i++){
            //每一行的所有的Views
            lineViews = mAllView.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的状态
                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();

                child.layout(lc,tc,rc,bc);
                left += child.getMeasuredWidth() + lp.rightMargin
                        + lp.leftMargin;

            }
            left = getPaddingLeft();
            top += lineHeight;
        }
        Log.v(TAG, "onLayout   mAllViews.size() -- > " + mAllView.size() + "   mLineHeight.size() -- > "+ mLineHeight.size());
    }
}

看懂不如写出来~

你可能感兴趣的:(自定义ViewGroup之流式布局)