LinearLayout自动换行自定义View

本文介绍了如何通过自定义View实现LinearLayout内容自动换行,并在布局中添加间隔,以达到更好的视觉效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文章增加了间隔



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

import com.wttech.gm.R;

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

public class WrapLinearLayout extends LinearLayout {
    private int widthGap = 30;//间隔
    private int heightGap = 20;

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

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

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


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if (getChildCount() == 0) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        //如果有子view,行数肯定至少1行
        int lineCount = 1;
        //此布局高度一般是wrap_content,所以需要对AT_MOST模式做处理
        if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
            //子View的宽度之和
            int childrenTotalWidth = 0;
            View childView = null;
            LinearLayout.LayoutParams params = null;
            //循环子View,分别测量子View的宽高
            for (int i = 0; i < getChildCount(); i++) {
                childView = getChildAt(i);
                if (childView.getVisibility() != GONE) {
                    params = ((LinearLayout.LayoutParams) childView.getLayoutParams());
                    //测量子View
                    measureChild(childView, widthMeasureSpec, heightMeasureSpec);
                    //把子View的宽度和margin属性做加和
                    childrenTotalWidth += childView.getMeasuredWidth() + params.leftMargin + params.rightMargin + widthGap;
                    //比较此layout的width和子View的总宽度
                    if (childrenTotalWidth - widthGap > width) {//条件成立,即折行 去除第一个左侧gap
                        //行数加1
                        lineCount++;
                        //子view比总宽度还大,截取一行
//                        if (childView.getMeasuredWidth() > width) {
//                            childrenTotalWidth = width + widthGap;
//                        } else {
                            //把子view的总宽度置为当前子view的宽度,以便后续的子view宽度的继续加和操作
                            childrenTotalWidth = childView.getMeasuredWidth() + params.leftMargin + params.rightMargin + widthGap;
                    }
                }
            }
            //循环结束,即可得到lineCount的值
            LayoutParams layoutParams = (LayoutParams) getChildAt(0).getLayoutParams();
            //注意这里设置的所有的子View的topMargin和bottomMargin分别一样。
            //由于子View的高度一致,所以取第一个子View的高度和其上下margin属性,乘以行数,即可得到layout在AT_MOST模式下总高度
            height = (getChildAt(0).getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin) * lineCount + (lineCount - 1) * heightGap;
        }

        setMeasuredDimension(width, height);
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (getChildCount() == 0) {
            super.onLayout(changed, l, t, r, b);
            return;
        }
        //子view的总宽度,用来做折行判断,并计算出所有位于每一行行首的子View的索引
        int currentLineTotalWidth = 0;
        //存储每一行行首子View的索引
        List<Integer> list = new ArrayList<>();
        //第一个子View肯定位于行首
        list.add(0);
        View child = null;
        LinearLayout.LayoutParams p = null;
        //当前行
        int currentLine = 0;
        for (int i = 0; i < getChildCount(); i++) {
            child = getChildAt(i);
            p = ((LayoutParams) child.getLayoutParams());
            currentLineTotalWidth += child.getMeasuredWidth() + p.leftMargin + p.rightMargin + widthGap;
            //同onMeasure方法中的判断,判断折行的位置
            if (currentLineTotalWidth - widthGap > getMeasuredWidth()) {//条件满足则折行
                //并把当前View的索引存储list中
                list.add(i);
                //重置currentLineTotalWidth
                currentLineTotalWidth = child.getMeasuredWidth() + p.leftMargin + p.rightMargin + widthGap;
//                //子View大于一行截取一行
//                if (currentLineTotalWidth > getMeasuredWidth()) {
//                    currentLineTotalWidth = getMeasuredWidth() + widthGap;
//                }
            }


            int left = 0;
            int top = 0;
            //设置当前行
            currentLine = list.size() - 1;
            //循环每一行的textView计算当前view的left
            for (int m = list.get(currentLine); m < i; m++) {
                left += getChildAt(m).getMeasuredWidth() + ((LayoutParams) getChildAt(m).getLayoutParams()).leftMargin + ((LayoutParams) getChildAt(m).getLayoutParams()).rightMargin + widthGap;
            }
            //计算出的left需要加上当前子View的leftMargin属性
            left += p.leftMargin;
            //注意这里设置的所有的子View的topMargin和bottomMargin分别一样。
            //top属性是由行数乘以行高,并加上当前View的top属性
            top = (getChildAt(0).getMeasuredHeight() + p.topMargin + p.bottomMargin) * currentLine + p.topMargin + currentLine * heightGap;
            //调用子view的layout方法去完成布局
            child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());

        }
    }
}


  <com.custom.WrapLinearLayout
                android:id="@+id/wll"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        for (int i = 0; i < 15; i++) {
            TextView textView = null;
            if (i % 2 == 0) {
                textView = new TextView(this);
                textView.setText("偶数" + i);
            } else if (i % 3 == 0) {
                textView = new TextView(this);
                textView.setText("奇数奇数奇数奇数" + i);
            }else if (i % 5 == 0) {
                textView = new TextView(this);
                textView.setSingleLine();
                textView.setEllipsize(TextUtils.TruncateAt.END);
                textView.setText("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + i);
            } else {
                textView = new TextView(this);
                textView.setText("超长的超长的超长的" + i);
            }
            wrapLinearLayout.addView(textView);
            final String str = textView.getText().toString();
            textView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    showToast(str);
                }
            });
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值