简单自定义二:自定义标签(流式布局)

自定义标签大家都已经不陌生了,这里记录下学习笔记

先看效果图

实现思路:

1. 继承ViewGroup,重写onMeasure和onLayout方法

2.当childview累计宽小于空间宽,叠加到当前行;

3.当childview累计宽大于空间宽,则换行显示;

4.如果是wrap_content,则宽为所有行宽的最大值,高为每行childview高的最大值之和,注意把childview的margin和ViewGroup的padding考虑进去

相关知识点

根据上一篇:简单自定义一 我们知道,自定义view可分为四种,这里属于直接继承ViewGroup,重写它里面的两个重要方法的:

1. onMeasure方法

a. 我们为什么要重写这个方法并重新计算控件的宽高呢?那时因为ViewGroup在MeasureSpec.AT_MOST模式下(宽/高设置为wrap_content),onMeasure方法给出的宽/高是match_content的宽/高的值,所以我们要重写这个方法去计算出ViewGroup的实际宽高。

b. 在计算ViewGroup的宽高时,我们需要去遍历它下面的所有childview,然后调用measureChild(childView, widthMeasureSpec, heightMeasureSpec)方法计算出childview的宽高值,这个值是包含了childview的padding在内的,单是不包含childview的margin值,所有我们还需要加上每个childview的margin值才行。

c. 最后调用setMeasuredDimension方法,把计算的实际宽高传入,测量出实际宽高。

 

2. onLayout方法

这个方法决定了childview的绘制位置,我们同样需要去遍历每个childview,计算它们相对于ViewGroup的边距,然后调用childView.layout(vl, vt, vr, vb)方法,最终确定每个childview的绘制位置。

 

最后放上代码,都有详细的注释

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

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

/**
 * 自定义标签(流式布局)
 */
public class FlowLayout extends ViewGroup {
    //存储所有子view对象
    private List<List<View>> mLineViews = new ArrayList<>();
    //记录每行的最大高度
    private List<Integer> mLineHeight = new ArrayList<>();

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

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

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

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

        //onMeasure会多次执行,清空记录
        mLineViews.clear();
        mLineHeight.clear();

        //获取测量的模式和大小
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        //记录ViewGroup真实的测量宽高
        int viewGroupWidth = getPaddingLeft() + getPaddingRight();
        int viewGroupHeight = getPaddingTop() + getPaddingBottom();


        //当前所占的宽高
        int currentLineWidth = 0;
        int currentLineHeight = 0;

        //存储每行的子View对象
        List<View> lineView = new ArrayList<>();
        //获得所有子View数
        int childViewsCount = getChildCount();
        for (int i = 0; i < childViewsCount; i++) {
            //获得所有子View对象
            View childView = getChildAt(i);
            //若GONE,则不计算进宽高
            if (childView.getVisibility() == GONE){
                continue;
            }

            //对子View进行测量
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
            //获得子View实际宽高,将margin考虑进去
            MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
            int childViewWidth = childView.getMeasuredWidth() + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin;
            int childViewHeight = childView.getMeasuredHeight() + marginLayoutParams.topMargin + marginLayoutParams.bottomMargin;

            //判断条件不要忽略了PaddingLeft、PaddingRight,否则设置padding绘制不完全
            if (currentLineWidth + childViewWidth +  getPaddingLeft() + getPaddingRight() > widthSize) {
                //换行
                viewGroupWidth = Math.max(currentLineWidth, widthSize);
                viewGroupHeight += currentLineHeight;
                //添加行高
                mLineHeight.add(currentLineHeight);
                //添加行对象
                mLineViews.add(lineView);

                //记录新的一行
                lineView = new ArrayList<View>();
                //在行对象里添加子View对象
                lineView.add(childView);
                //重置行宽
                currentLineWidth = childViewWidth;
            } else {
                //不换行
                currentLineWidth += childViewWidth;
                currentLineHeight = Math.max(currentLineHeight, childViewHeight);
                //添加行对象里的子View
                lineView.add(childView);
            }

            if (i == childViewsCount - 1) { //最后一个子View的时候
                //添加行对象
                mLineViews.add(lineView);
                viewGroupWidth = Math.max(childViewWidth, viewGroupWidth);
                viewGroupHeight += childViewHeight;
                //添加行高
                mLineHeight.add(currentLineHeight);
            }
        }

        if (widthMode != MeasureSpec.AT_MOST) {
            viewGroupWidth = widthSize;
        }
        if (heightMode != MeasureSpec.AT_MOST) {
            viewGroupHeight = heightSize;
        }
        //重新测量,获得ViewGroup实际的宽高
        setMeasuredDimension(viewGroupWidth, viewGroupHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //考虑控件的padding
        int left = getPaddingLeft();
        int top = getPaddingTop();
        //行数
        int lines = mLineViews.size();
        for (int i = 0; i < lines; i++) {
            //每行行高
            int lineHeight = mLineHeight.get(i);
            //行内子View数
            List<View> viewList = mLineViews.get(i);
            int views = viewList.size();

            for (int j = 0; j < views; j++) {
                View childView = viewList.get(j);
                //若GONE,则不计算进宽高
                if (childView.getVisibility() == GONE){
                    continue;
                }

                MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
                int vl = left + marginLayoutParams.leftMargin;
                int vt = top + marginLayoutParams.topMargin;
                //vr和vb在vl、vt的基础上计算
                int vr = vl + childView.getMeasuredWidth();
                int vb = vt + childView.getMeasuredHeight();
                childView.layout(vl, vt, vr, vb);
                //累计当前行的left位置
                left += childView.getMeasuredWidth() + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin;
            }
            //重置行的left位置
            left = getPaddingLeft();
            //累计top位置
            top += lineHeight;
        }
    }

    /**
     * 指定ViewGroup的LayoutParams,将子View的margin考虑进去
     *
     * @param attrs
     * @return
     */
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值