zoukankan      html  css  js  c++  java
  • 自定义标签换行控件WordWrapperView

        遇到多项选择的情况,我们可以使用RadioGroup,但是它只能处理单行或者单列的情况,如果标签很多就需要滑动屏幕了。考虑到显示的简洁性,我们也可以考虑自定义一个换行控件。这里就记录一下我实现的换行控件WordWrapperView。

        一、大致思路:

        WordWrapperView扩展自ViewGroup,根据内容标签测量尺寸,确定内容标签的位置。

        二、实现:

        WordWrapperView类代码:

    /**
     * 自动换行的空间,只支持自动换行<br/>
     * 1.没有考虑单个标签过长的情况<br/>
     * 2.不支持滚动,即标签过多时不支持上下滑动<br/>
     * 3.默认标签的高度都是一样的<br/>
     * Created by hsji on 16/1/9.
     */
    public class WordWrapperView extends ViewGroup {
        private static final String TAG = WordWrapperView.class.getSimpleName();
        private static final int DEFAULT_HORIZONTAL_SPACING = 10;
        private static final int DEFAULT_VERTICAL_SPACING = 10;
        /**
         * 水平间距
         */
        private int mHorizontalSpacing;
        /**
         * 垂直间距
         */
        private int mVerticalSpacing;
    
    
        public WordWrapperView(Context context) {
            this(context, null);
        }
    
        public WordWrapperView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public WordWrapperView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            //通过 TypedArray 获取自定义属性
            TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.WordWrapperView);
            //获取自定义属性的个数
            int N = typedArray.getIndexCount();
            for (int i = 0; i < N; i++) {
                int attr = typedArray.getIndex(i);
                switch (attr) {
                    case R.styleable.WordWrapperView_horizontal_spacing:
                        //通过自定义属性拿到水平间距
                        mHorizontalSpacing = typedArray.getDimensionPixelSize(attr, DEFAULT_HORIZONTAL_SPACING);
                        break;
                    case R.styleable.WordWrapperView_vertical_spacing:
                        //通过自定义属性拿到垂直间距
                        mVerticalSpacing = typedArray.getDimensionPixelSize(attr, DEFAULT_VERTICAL_SPACING);
                        break;
                }
            }
            //使用完成之后记得回收
            typedArray.recycle();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            measureChildren(widthMeasureSpec, heightMeasureSpec);
            //必须先测量出宽度,之后才能测量高度
            int width = measureWidth(widthMeasureSpec);
            //根据测量出的宽度,测量高度
            int height = measureHeight(heightMeasureSpec, width);
            setMeasuredDimension(width, height);
        }
    
        /**
         * 计算所有标签排成一行所需要的宽度,再根据mode处理
         *
         * @param widthMeasureSpec
         * @return
         */
        private int measureWidth(int widthMeasureSpec) {
            int mode = MeasureSpec.getMode(widthMeasureSpec);
            int size = MeasureSpec.getSize(widthMeasureSpec);
    
            int desired = getPaddingLeft();
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                int childWidth = child.getMeasuredWidth();
                if (desired == getPaddingLeft()) {
                    desired += childWidth;
                } else {
                    desired += mHorizontalSpacing + childWidth;
                }
            }
            desired += getPaddingRight();
            if (mode == MeasureSpec.EXACTLY) {
                return size;
            } else {
                if (mode == MeasureSpec.AT_MOST) {
                    return Math.min(desired, size);
                } else {
                    return desired;
                }
            }
        }
    
        /**
         * 先计算出需要排多少行,根据行数算出高度,再根据mode处理
         *
         * @param heightMeasureSpec
         * @param width
         * @return
         */
        private int measureHeight(int heightMeasureSpec, int width) {
            int mode = MeasureSpec.getMode(heightMeasureSpec);
            int size = MeasureSpec.getSize(heightMeasureSpec);
    
            int counter = 0;//rows
            int totalWidth = getPaddingLeft();
            int count = getChildCount();
            int childHeight = 0;
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                int childWidth = child.getMeasuredWidth();
                //第一行第一列的标签
                if (totalWidth == getPaddingLeft()) {
                    totalWidth += childWidth;
                    childHeight = child.getMeasuredHeight();
                } else {
                    //第一列但非第一行的标签
                    if (totalWidth + mHorizontalSpacing + childWidth <= width - getPaddingRight()) {
                        totalWidth += mHorizontalSpacing + childWidth;
                    } else {//非第一列的标签
                        counter++;
                        totalWidth = getPaddingLeft() + childWidth;
                    }
                }
            }
            int desired = getPaddingTop() + childHeight + (mVerticalSpacing + childHeight) * counter + getPaddingBottom();
            if (mode == MeasureSpec.EXACTLY) {
                return size;
            } else {
                if (mode == MeasureSpec.AT_MOST) {
                    return Math.min(desired, size);
                } else {
                    return desired;
                }
            }
        }
    
        /**
         * 采用两个变量totalWidth和totalHeight来记录坐标,考虑的情况和measureHeight类似
         *
         * @param changed
         * @param l
         * @param t
         * @param r
         * @param b
         */
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            if (changed) {
                int count = getChildCount();
                int totalWidth = getPaddingLeft();
                int totalHeight = getPaddingTop();
                int childHeight = 0;
                for (int i = 0; i < count; i++) {
                    View child = getChildAt(i);
                    childHeight = child.getMeasuredHeight();
                    if (totalWidth == getPaddingLeft()) {
                        child.layout(totalWidth,
                                totalHeight,
                                totalWidth + child.getMeasuredWidth(),
                                totalHeight + child.getMeasuredHeight());
                        totalWidth += child.getMeasuredWidth();
                    } else {
                        if (totalWidth + mHorizontalSpacing + child.getMeasuredWidth() <= r - l - getPaddingRight()) {
                            child.layout(totalWidth + mHorizontalSpacing,
                                    totalHeight,
                                    totalWidth + mHorizontalSpacing + child.getMeasuredWidth(),
                                    totalHeight + child.getMeasuredHeight());
                            totalWidth += mHorizontalSpacing + child.getMeasuredWidth();
                        } else {
                            totalWidth = getPaddingLeft();
                            totalHeight += mVerticalSpacing + childHeight;
                            child.layout(totalWidth, totalHeight, totalWidth + child.getMeasuredWidth(), totalHeight + child.getMeasuredHeight());
                            totalWidth += child.getMeasuredWidth();
                        }
                    }
                }
            }
        }
    }
    

     自定义属性:

    <declare-styleable name="WordWrapperView">
        <attr name="horizontal_spacing" format="dimension"></attr>
        <attr name="vertical_spacing" format="dimension"></attr>
    </declare-styleable>
    

     三、使用

    <com.hsji.testapp.widget.WordWrapperView
            android:id="@+id/wrapper"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp"
            app:horizontal_spacing="10dp"
            app:vertical_spacing="20dp">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_btn_round_corner"
                android:padding="8dp"
                android:text="标签1"
                android:textColor="@android:color/white" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_btn_round_corner"
                android:padding="8dp"
                android:text="标签2"
                android:textColor="@android:color/white" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_btn_round_corner"
                android:padding="8dp"
                android:text="标签3"
                android:textColor="@android:color/white" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_btn_round_corner"
                android:padding="8dp"
                android:text="标签4"
                android:textColor="@android:color/white" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_btn_round_corner"
                android:padding="8dp"
                android:text="标签5"
                android:textColor="@android:color/white" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_btn_round_corner"
                android:padding="8dp"
                android:text="标签6"
                android:textColor="@android:color/white" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_btn_round_corner"
                android:padding="8dp"
                android:text="标签7"
                android:textColor="@android:color/white" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_btn_round_corner"
                android:padding="8dp"
                android:text="标签8"
                android:textColor="@android:color/white" />
    
        </com.hsji.testapp.widget.WordWrapperView>
    

     以上是直接在布局文件里面设置标签,也可以通过addView的方式往WordWrapperView里面添加标签。

    另外可以为内容标签设置点击事件(tv.setOnClickListener(listener)),这里就不再赘述。

    最后贴一张效果图:

     

  • 相关阅读:
    springcloud概述
    springcloud-微服务架构基础
    TypeScript 教程
    提示工具以及弹出框
    Bootstrap 弹出框(Popover)插件
    JavaScript JSON
    JavaScript常见基础函数
    7种JavaScript代码调试的方法
    Bootstrap 网格系统
    文本元素
  • 原文地址:https://www.cnblogs.com/hsji/p/5117641.html
Copyright © 2011-2022 走看看