Android 从 0 开始学习自定义 View(八) 自定义流式布局

前言

例子是用来理解自定义 ViewGroup 流程,不建议直接使用,如需使用可根据需求进行修改。

效果图
实现思路
  1. 继承自 ViewGroup
  2. 实现 onMeasure 方法,通过子 View 的宽高来确定自己的宽高
  3. 实现 onLayout 方法,摆放子 View 的位置
TagLayout
public class TagLayout extends ViewGroup {
    public TagLayout(Context context) {
        this(context, null);
    }
    
    public TagLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}
onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mChildViews.clear();
    //获取子View的个数
    int childCount = getChildCount();
    //父View的宽度
    int width = MeasureSpec.getSize(widthMeasureSpec);
    //需要计算的高度
    int height = getPaddingTop() + getPaddingBottom();
    //单行的宽度
    int lineWidth = getPaddingLeft();
    //存放一行的View
    List childViews = new ArrayList<>();
    mChildViews.add(childViews);
    //处理单行不换行情况
    int maxHeight = 0;
    //循环测量子View
    for (int i = 0; i < childCount; i++) {
        View childView = getChildAt(i);
        //测量子View的宽高
        measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        //获取子View的 margin
        MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
        //子View占据的宽度
        int childWidth = childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
        //子View占据的高度
        int childHeight = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
        //根据子View的宽度来处理换行
        if (lineWidth + childWidth > width) {
            //换行
            height += maxHeight;
            lineWidth = childWidth;
            childViews = new ArrayList<>();
            mChildViews.add(childViews);
        } else {
            //累加宽度
            lineWidth += childWidth;
            maxHeight = Math.max(childHeight, maxHeight);
        }
        childViews.add(childView);
    }
    height += maxHeight;
    //设置宽高
    setMeasuredDimension(width, height);
}

onMeasure 方法需要记录换行的信息,每一行有多少个子 View,最终确定自己的高度

onLayout
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int left;
    int top = getPaddingTop();
    int right;
    int bottom;
    int maxHeight = 0;
    for (List childViews : mChildViews) {
        left = getPaddingLeft();
        for (View childView : childViews) {
            //获取子View的 margin
            MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
            left += params.leftMargin;
            int childTop = top + params.topMargin;
            right = left + childView.getMeasuredWidth();
            bottom = childTop + childView.getMeasuredHeight();
            Log.d("TAG", "left -> " + left + " top ->" + top + " right -> " + right + " bottom ->" + bottom);
            //摆放
            childView.layout(left, childTop, right, bottom);
            left += childView.getMeasuredWidth() + params.rightMargin;
            int childHeight = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
            maxHeight = Math.max(childHeight, maxHeight);
        }
        top += maxHeight;
    }
}

便利每行子 View 集合,设置子 View 摆放的位置,如果一行不止一个子 View 需要将 left 累加。一行行便利,最终摆放出正确的位置

自定义流式布局就介绍到这里了,如果有什么写得不对的,可以在下方评论留言,我会第一时间改正。

Github 源码链接

你可能感兴趣的:(Android 从 0 开始学习自定义 View(八) 自定义流式布局)