Android实现热词显示,按照字数宽度不规则布局

package com.test.views;

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

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

/**
 * 类名:
 * 说明:
 * <p>
 */

public class FlowLayout extends ViewGroup {

    private static final String TAG = "FlowLayout";

    private List<Rect> mChildrenPositionList = new ArrayList<>();   // 记录各子 View 的位置
    private int mMaxLines = Integer.MAX_VALUE;      // 最多显示的行数,默认无限制
    private int mVisibleItemCount;       // 可见的 item 数

    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) {
        // 清除之前的位置
        mChildrenPositionList.clear();
        // 测量所有子元素(这样 child.getMeasuredXXX 才能获取到值)
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//        Log.d(TAG,"widthSize==="+widthSize);
//        Log.d(TAG,"widthMode==="+widthMode);
//        Log.d(TAG,"heightSize==="+heightSize);
//        Log.d(TAG,"heightMode==="+heightMode);
//        Log.d(TAG,"MeasureSpec.EXACTLY==="+MeasureSpec.EXACTLY); // 1073741824
//        Log.d(TAG,"MeasureSpec.AT_MOST==="+MeasureSpec.AT_MOST); //-2147483648
//        Log.d(TAG,"MeasureSpec.UNSPECIFIED==="+MeasureSpec.UNSPECIFIED);

        int[] a = helper(widthSize);
//        Log.d(TAG,"widthSize a[0]==="+ a[0]);
//        Log.d(TAG,"widthSize a[1]==="+ a[1]);
        int measuredHeight = 0;
        // EXACTLY 模式:对应指定大小和 match_parent
        if (heightMode == MeasureSpec.EXACTLY) {
            measuredHeight = heightSize;
        }
        // AT_MOST 模式,对应 wrap_content
        else if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
            measuredHeight = a[0];
        }
        int measuredWidth = 0;
        if (widthMode == MeasureSpec.EXACTLY) {
            measuredWidth = widthSize;
        }
        else if (widthMode == MeasureSpec.AT_MOST  || widthMode == MeasureSpec.UNSPECIFIED) {
            measuredWidth = a[1];
        }
//        Log.d(TAG,"widthSize measuredWidth=="+ measuredWidth +"  measuredHeight="+measuredHeight);
//        Log.d(TAG,"***********************************************");
        setMeasuredDimension(measuredWidth, measuredHeight);
    }

    /**
     * 在 wrap_content 情况下,得到布局的测量高度和测量宽度
     * 返回值是一个有两个元素的数组 a,a[0] 代表测量高度,a[1] 代表测量宽度
     */
    private int[] helper(int widthSize) {
        boolean isOneRow = true;    // 是否是单行
        int width = getPaddingLeft();   // 记录当前行已有的宽度
        int height = getPaddingTop();   // 记录当前行已有的高度
        int maxHeight = 0;      // 记录当前行的最大高度
        int currLine = 1;       // 记录当前行数

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            // 获取当前子元素的 margin
            LayoutParams params = child.getLayoutParams();
            MarginLayoutParams mlp;
            if (params instanceof MarginLayoutParams) {
                mlp = (MarginLayoutParams) params;
            } else {
                mlp = new MarginLayoutParams(params);
            }
            // 记录子元素所占宽度和高度
            int childWidth = mlp.leftMargin + child.getMeasuredWidth() + mlp.rightMargin;
            int childHeight = mlp.topMargin + child.getMeasuredHeight() + mlp.bottomMargin;
            maxHeight = Math.max(maxHeight, childHeight);

            // 判断是否要换行
            if (width + childWidth + getPaddingRight() > widthSize) {
                // 加上该行的最大高度
                height += maxHeight;
                // 重置 width 和 maxHeight
                width = getPaddingLeft();
                maxHeight = childHeight;
                isOneRow = false;
                currLine++;
                if (currLine > mMaxLines) {
                    break;
                }
            }
            // 存储该子元素的位置,在 onLayout 时设置
            Rect rect = new Rect(width + mlp.leftMargin,
                    height + mlp.topMargin,
                    width + childWidth - mlp.rightMargin,
                    height + childHeight - mlp.bottomMargin);
            mChildrenPositionList.add(rect);

            // 加上该子元素的宽度
            width += childWidth;
        }

        int[] res = new int[2];
        res[0] = height + maxHeight + getPaddingBottom();
        res[1] = isOneRow? width + getPaddingRight() : widthSize;

        return res;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 布置子 View 的位置
        int n = Math.min(getChildCount(), mChildrenPositionList.size());
        for (int i = 0; i < n; i++) {
            View child = getChildAt(i);
            Rect rect = mChildrenPositionList.get(i);
            child.layout(rect.left, rect.top, rect.right, rect.bottom);
        }
        mVisibleItemCount = n;
    }

    /**
     * 设置 Adapter
     */
    public void setAdapter(Adapter adapter) {
        // 移除之前的视图
        removeAllViews();
        // 添加 item
        int n = adapter.getItemCount();
        for (int i = 0; i < n; i++) {
            ViewHolder holder = adapter.onCreateViewHolder(this);
            adapter.onBindViewHolder(holder, i);
            View child = holder.itemView;
            addView(child);
        }
    }

    /**
     * 设置最多显示的行数
     */
    public void setMaxLines(int maxLines) {
        mMaxLines = maxLines;
    }

    /**
     * 获取显示的 item 数
     */
    public int getVisibleItemCount() {
        return mVisibleItemCount;
    }

    public abstract static class Adapter<VH extends ViewHolder> {

        public abstract VH onCreateViewHolder(ViewGroup parent);

        public abstract void onBindViewHolder(VH holder, int position);

        public abstract int getItemCount();

    }

    public abstract static class ViewHolder {
        public final View itemView;

        public ViewHolder(View itemView) {
            if (itemView == null) {
                throw new IllegalArgumentException("itemView may not be null");
            }
            this.itemView = itemView;
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值