前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 自定义流布局。使用开源库SimpleFlowLayout

Android 自定义流布局。使用开源库SimpleFlowLayout

作者头像
张云飞Vir
发布2020-03-16 15:59:45
4870
发布2020-03-16 15:59:45
举报
文章被收录于专栏:写代码和思考

前言

实际项目中需要实现一个 热门搜索 的栏目,类似下图: 由于 子项(子view) 中的文字是可变的,一行能显示的 子项 的个数也无法确定。需要支持自动换行和计算位置。

�# 使用开源类库SimpleFlowLayout

我自己写了个 自定义view ,继承自viewGroup, 来实现它,托管到github开源平台。

代码语言:javascript
复制
名称:SimpleFlowLayout
地址:https://github.com/vir56k/SimpleFlowLayout
特点:可以不断添加多个子view,计算位置,自动换行。 类似html中的div标签
适用: 热门标签

实现思路

要实现 自定义的viewgroup,需要:

  1. 继承自 ViewGroup
  2. 实现 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)   这个方法用于测量 自己(自定义view)本身需要的宽度和高度
  3. 实现 protected void onLayout(boolean changed, int l, int t, int r, int b)   这个方法用于指定如何摆放 子view 的位置。

实现代码

代码语言:javascript
复制
package zhangyf.vir56k.flowframelayout;

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

/**
 * name: android 简单的流布局自定义view
 * 作者:张云飞vir
 * 特点:可以不断添加多个子view,计算位置,自动换行。
 * 适用: 热门标签
 * Created by zhangyunfei on 15/12/4.
 */
public class SimpleFlowLayout extends ViewGroup {
    public SimpleFlowLayout(Context context) {
        super(context);
    }

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

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

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


        int widthNeed = 0;
        int heightNeed = 0;
        int x = 0;
        int y = 0;
        int currentLineHeight = 0;
        View child;
        for (int i = 0; i < getChildCount(); i++) {
            child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                continue;
            }

            child.measure(widthMeasureSpec, heightMeasureSpec);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//获得子view的 外边距
            //测算子view宽度,本行这句代码有问题,不能计算子view的自动换行 int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            //使用viewGroup的measureChildWithMargins测算宽度,在这个方法里处理了 LayoutParams的match_parent等方式的处理
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;

            if (x + childWidth > widthMax) {//换行处理,本行高度和x轴都清零,y轴下移(加上上次的行高)
                y += currentLineHeight;
                currentLineHeight = 0;
                x = 0;
            }
            x += childWidth;
            currentLineHeight = Math.max(currentLineHeight, childHeight);

            widthNeed = Math.max(widthNeed, x);//加入了这个 子view后,留下最大宽度
            heightNeed = Math.max(heightNeed, y + currentLineHeight);//对比上次的,留下最大的高度
        }
        setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthMax : widthNeed,
                heightMode == MeasureSpec.EXACTLY ? heightMax : heightNeed);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int widthMax = getWidth();
        int x, y;
        x = 0;
        y = 0;
        View child;
        int left = 0;
        int top = 0;
        int currentLineHeight = 0;
        for (int i = 0; i < getChildCount(); i++) {
            child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                continue;
            }
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            if (x + childWidth > widthMax) {//换行处理
                y += currentLineHeight;
                x = 0;
                currentLineHeight = 0;
            }
            left = x + lp.leftMargin;
            top = y + lp.topMargin;
            //定位子view的位置
            child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());

            x += childWidth;
            currentLineHeight = Math.max(currentLineHeight, childHeight);
        }
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 实现思路
  • 实现代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档