Android标签流布局设计

Android中,标签流布局就是按照从左到右,从上到下的布局方式显示标签,当位置不够标签长度的时候,就换行显示。实现的原理也比较简单,就是先设计行管理器,其中行管理器主要负责获取行高以及添加子子View进行里。之后在ViewGroup中的onMeasure方法中对行进行计算得出总的高度,重新测量设置ViewGroup的宽高。


这里主要现在values目录下创建attr文件,进行自定义View的属性声明。

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="FlowTagLayout">
       <attr name="horizontalSpacing" format="dimension" />
       <attr name="verticalSpacing" format="dimension" />
   </declare-styleable>
</resources>

再实现一个标签的样式,在FlowTagLayout种的setFlowTagLayout方法中使用到,这里实现一个简单的TextView显示标签,使用圆角矩形作为背景。

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="item"
    android:padding="5dp"
    android:background="@drawable/shape_item_bg">

</TextView>


标签背景:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <corners android:radius="5dp" />
    <stroke android:color="#e25b4f" android:width="1dp" />
    <solid android:color="@android:color/transparent" />
</shape>


自定义FlowTagLayout的具体实现如下:

public class FlowTagLayout extends ViewGroup{

    private int horizontalSpacing = dip2px(10); //水平方向上标签之间的间隔
    private int verticalSpacing = dip2px(10); //行距
    private Line mCurrLine;
    private List<Line> mLines = new ArrayList<>();
    private int mLineSize;


    public FlowTagLayout(Context context) {
        this(context, null);
    }

    public FlowTagLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FlowTagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FlowTagLayout, defStyleAttr, 0);
        horizontalSpacing = ta.getDimensionPixelSize(R.styleable.FlowTagLayout_horizontalSpacing, horizontalSpacing);
        verticalSpacing = ta.getDimensionPixelSize(R.styleable.FlowTagLayout_verticalSpacing, verticalSpacing);
        ta.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //实际可用高度
        int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
        int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
        int widthModel = MeasureSpec.getMode(widthMeasureSpec);
        int heightModel = MeasureSpec.getMode(heightMeasureSpec);

        //初始化
        mLines.clear();
        mLineSize = 0;
        mCurrLine = new Line();

        for (int i = 0; i < getChildCount(); i++){
            //测量子View的高度
            View childView = getChildAt(i);
            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, widthModel == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : widthModel);
            int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, heightModel == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : heightModel);
            childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            mLineSize += childView.getMeasuredWidth();
            if (mLineSize < width){
                mCurrLine.addView(childView);
                mLineSize += horizontalSpacing;
            }else{ //换行
                if (mCurrLine != null){
                    mLines.add(mCurrLine);
                }
                mCurrLine = new Line();
                mLineSize = 0;
                mCurrLine .addView(childView);

                mLineSize += childView.getMeasuredWidth() + horizontalSpacing;
            }
        }

        //加上最后一行
        if (mCurrLine != null && !mLines.contains(mCurrLine)){
            mLines.add(mCurrLine);
        }

        int totalHeight = 0;
        for (int i = 0; i < mLines.size(); i++){
            totalHeight += mLines.get(i).getLineHeight();
        }

        totalHeight += verticalSpacing * (mLines.size() - 1);
        totalHeight += getPaddingBottom() + getPaddingTop();
        //重测高度
        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), resolveSize(totalHeight, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        left = getPaddingLeft();
        top = getPaddingTop();
        for (int i = 0; i < mLines.size(); i++){
            Line line = mLines.get(i);
            line.layout(left, top);
            top += line.getLineHeight() + verticalSpacing;
        }
    }

    /**
     * 行管理器, 管理每一行的孩子
     */
    private class Line{
        private List<View> lineViews = new ArrayList<>();
        int maxHeight;

        private void addView(View view){
            lineViews.add(view);
            if (maxHeight < view.getMeasuredHeight()){
                maxHeight = view.getMeasuredHeight();
            }
        }

        /**
         * 指定绘制子View的位置
         * @param left 左上角x轴坐标
         * @param top 左上角y轴坐标
         */
        private void layout(int left, int top){
            int currLeft = left;
            for (View view : lineViews){
                view.layout(currLeft, top, currLeft + view.getMeasuredWidth(), top + view.getMeasuredHeight());
                currLeft += (view.getMeasuredWidth() + horizontalSpacing);
            }
        }

        private int getLineHeight(){
            return maxHeight;
        }
    }


    public void setFlowTagLayout(List<String> dataList, final OnItemClickListener onItemClickListener){
        for (final String tag : dataList){
            TextView tv = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.layout_item_tag, null);
            tv.setText(tag);
            this.addView(tv, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            tv.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (onItemClickListener != null){
                        onItemClickListener.onItemClick(tag);
                    }
                }
            });
        }
    }

    //对外开放接口
    public interface OnItemClickListener{
        void onItemClick(String tag);
    }

    private int dip2px(float dip){
        float scale = getContext().getResources().getDisplayMetrics().density;
        return (int)(scale * dip + 0.5);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值