基于ViewGroup实现自动换行标签控件

来源:互联网 发布:中文域名到期查询 编辑:程序博客网 时间:2024/06/05 18:25
在上一篇博文 基于RelativeLayout实现自动换行标签控件 中,采用了继承RelativeLayout来实现,主要是对addRule()方法的使用。

我们也可以采用继承ViewGroup来达到同样的效果。

先上效果图。


实现思路:
标签View占据的是一个矩形空间,其在父视图ViewGroup中布局位置时,是通过它的四条边距离父视图的左边距和上边距来决定的。我们只要能够计算出,每个标签左边、上边、右边和下边距离父视图的距离即可。

如图所示。

下面是代码的实现,主要是重写onMeasure()方法和onLayout()方法。

首先是在实现中会用到的一些方法的介绍。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {}
该方法是View类中的方法,测量View的宽度和高度。

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {}
该方法是View类中的方法,必须在onMeasure()方法中调用,用来设置计算出来的当前View的宽高。

protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
该方法是ViewGroup类中的抽象方法,当我们继承ViewGroup时必须实现该方法。参数l,t,r,b表示该ViewGroup所占据的矩形空间的left,top,right,bottom。

public void layout(int l, int t, int r, int b) {}
该方法是View类中的方法,一个View对象调用该方法可以通过传入的参数left,top,right,bottom来放置它的位置,方法中的四个参数都是相对于父容器而言的。

然后来实现onMeasure()方法。
@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        mChildRects.clear();        // 整个TagLayout的宽度为固定值        int layoutWidth = MeasureSpec.getSize(widthMeasureSpec);        // 整个TagLayout的高度,初始为0,        int layoutHeight = 0;        // child的数量        int childCount = getChildCount();        // child view距离父视图的左边距        int childLeft = 0;        // child view距离父视图的上边距        int childTop = 0;        // 遍历child view        for (int i = 0; i < childCount; i++) {            View child = getChildAt(i);            // 测量child view,获取宽度和高度            child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);            int childWidth = child.getMeasuredWidth();            int childHeight = child.getMeasuredHeight();            // 如果child view的左边距 + 自身宽度 > 父视图宽度,需要换行            if (childLeft + childWidth > layoutWidth) {                // child view的左边距重置为0                childLeft = 0;                // child view的上边距 = 自身的上边距 + 自身宽度 + 垂直间距                childTop = childTop + childHeight + CHILD_VERTICAL_MARGIN;            }            // 将child view的Rect添加到集合中,在onLayout()方法中使用            mChildRects.add(new Rect(childLeft, childTop, childLeft + childWidth, childTop + childHeight));            // childLeft需要在已有的基础上,加上自身宽度和水平间距,进入下一轮循环使用            childLeft += (childWidth + CHILD_HORIZONTAL_MARGIN);            // 当循环到最后一个child view时,已有的childTop加上child view的自身高度,就是整个父视图的高度            if (i == childCount - 1) {                layoutHeight = childTop + childHeight;            }        }        // 最后别忘了调用setMeasuredDimension        setMeasuredDimension(layoutWidth, layoutHeight);    }

最后是onLayout()方法。
@Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        // 遍历已经计算出来的所有的child view的位置数据,对child view进行布局        int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            View child = getChildAt(i);            Rect location = mChildRects.get(i);            child.layout(location.left, location.top, location.right, location.bottom);        }    }

完整类的代码如下。
package net.csdn.blog.ruancoder;import android.content.Context;import android.graphics.Rect;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class TagLayout extends ViewGroup {    private List<Rect> mChildRects = new ArrayList<>();    private static final int CHILD_HORIZONTAL_MARGIN = 15;// 水平间距    private static final int CHILD_VERTICAL_MARGIN = 20;// 垂直间距    public TagLayout(Context context) {        super(context);    }    public TagLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    public TagLayout(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    public void setData(List<String> tags) {        for (String tag : tags) {            View view = LayoutInflater.from(getContext()).inflate(R.layout.tag, null);            TextView textView = (TextView) view.findViewById(R.id.text);            textView.setText(tag);            addView(view);        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        mChildRects.clear();        int layoutWidth = MeasureSpec.getSize(widthMeasureSpec);        int layoutHeight = 0;        int childCount = getChildCount();        int childLeft = 0;        int childTop = 0;        for (int i = 0; i < childCount; i++) {            View child = getChildAt(i);            child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);            int childWidth = child.getMeasuredWidth();            int childHeight = child.getMeasuredHeight();            if (childLeft + childWidth > layoutWidth) {                childLeft = 0;                childTop = childTop + childHeight + CHILD_VERTICAL_MARGIN;            }            mChildRects.add(new Rect(childLeft, childTop, childLeft + childWidth, childTop + childHeight));            childLeft += (childWidth + CHILD_HORIZONTAL_MARGIN);            if (i == childCount - 1) {                layoutHeight = childTop + childHeight;            }        }        setMeasuredDimension(layoutWidth, layoutHeight);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            View child = getChildAt(i);            Rect location = mChildRects.get(i);            child.layout(location.left, location.top, location.right, location.bottom);        }    }}

最后附上Demo工程下载链接:
http://download.csdn.net/detail/ruancoder/9596528
0 0
原创粉丝点击