自定义View之简版流式布局

先上源码

package com.example.view2;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

/**
 * 项目名称 View2
 * 创建人 
 * 创建时间 10/2/21 4:37 PM
 **/
class MyFlowLayout extends ViewGroup {
final String TAG = "MyFlowLayout";

    // 每行中的view on 10/2/21 4:48 PM
    private List<View> rowList = new ArrayList<>();
    // 所有的view on 10/2/21 4:49 PM
    private List<List<View>> lists = new ArrayList<>();
    private List<Integer> heights = new ArrayList<>();
    private List<Integer> widths = new ArrayList<>();
    // 当前行已添加View的宽度和 on 10/2/21 4:50 PM
    int currentRowSize = 0;


    public MyFlowLayout(Context context) {
        super(context);
    }

    public MyFlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        lists.clear();
        rowList.clear();
        currentRowSize = 0;

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

        int currentMaxHeight = 0;
        int childCount = getChildCount();
        for(int i=0;i<childCount;i++){
            View view = getChildAt(i);
            int childWidthSpec = getChildMeasureSpec(widthMeasureSpec,0,view.getLayoutParams().width);
            int childHeightSpec = getChildMeasureSpec(heightMeasureSpec,0,view.getLayoutParams().height);
            view.measure(childWidthSpec,childHeightSpec);
            currentRowSize = currentRowSize + view.getMeasuredWidth();
            currentMaxHeight = Math.max(currentMaxHeight,view.getMeasuredHeight());
            if(currentRowSize > widthSize){
                widths.add(currentRowSize - view.getMeasuredWidth());
                currentRowSize = view.getMeasuredWidth();
                lists.add(rowList);
                heights.add(currentMaxHeight);
                rowList = new ArrayList<>();
            }
            rowList.add(view);
            view.measure(childWidthSpec,childWidthSpec);
        }
        if(rowList.size() > 0){
            lists.add(rowList);
            widths.add(currentRowSize);
            heights.add(currentMaxHeight);
        }

        int width = 0;
        int height = 0;
        switch (widthMode){
            case MeasureSpec.EXACTLY:
                width = widthSize;
                break;
            case MeasureSpec.AT_MOST:
                for(int i=0;i<widths.size();i++){
                    width = Math.max(width,widths.get(i));
                }
                break;
            case MeasureSpec.UNSPECIFIED:
                width = widthSize;
                break;

        }

        switch (heightMode){
            case MeasureSpec.EXACTLY:
                height = heightSize;
                break;
            case MeasureSpec.AT_MOST:
                for(int i=0;i<heights.size();i++){
                    height = height + heights.get(i);
                }
                break;
            case MeasureSpec.UNSPECIFIED:
                height = heightSize;
                break;

        }
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int leftOffset = 0;
        int topOffset = 0;
        for(int i=0;i<lists.size();i++){
            rowList = lists.get(i);
            leftOffset = 0;
            for(int j=0;j<rowList.size();j++){
                View view = rowList.get(j);
                view.layout(
                        leftOffset,
                        topOffset,
                        leftOffset + view.getMeasuredWidth(),
                        topOffset + view.getMeasuredHeight());
                leftOffset = leftOffset + view.getMeasuredWidth();
            }
            topOffset = topOffset + heights.get(i);
        }
    }


}
复制代码

思路说明

测量:

  • 遍历子View,父容器的MeasureSpec和子View的布局参数,测量子View。
  • 在测量过程中,记录每一个View的宽和高。
  • 遍历过程中累加遍历的view的宽度值,如果超过了父容器的宽度,换行。
  • 换行时,将当前行的View中的高度的最大值保存。

布局:

  • 遍历保存的每一行的View,计算left、top、right和bottom,为其定位。

image.png

问题 在自定义ViewGroup中,如果ViewGroup的宽度为wrap_content,ViewGroup最终的尺寸如何确定。

猜你喜欢

转载自juejin.im/post/7014406638438350856
今日推荐