使用

.setLayoutManager(new FlowLayoutManager());
测试数据
public static List<TemplateTag> getTemplateTagList() {
	List<TemplateTag> list = new ArrayList<>();
	int colors[] = new int[10];
	
	for (int i = 0, j = colors.length; i < j; i++) {
		colors[i] = 0xFF000000 + new Random().nextInt(0xFFFFFF);
		//注意,Integer.MAX_VALUE= 0x7fffffff,超过这个值就是负数了,所以通过随机数获取int值时,传入的值要小于0x7fffffff
	}
	list.add(new TemplateTag("白乾涛", colors[0]));
	list.add(new TemplateTag("tag", colors[1]));
	list.add(new TemplateTag("标签 tag", colors[2]));
	list.add(new TemplateTag("1", colors[3]));
	list.add(new TemplateTag("颜色 " + colors[4], colors[4]));
	list.add(new TemplateTag("包青天", colors[5]));
	list.add(new TemplateTag("包青天包青天", colors[6]));
	list.add(new TemplateTag("包青天包青天包青天包青天包青天包青天包青天", colors[7]));
	list.add(new TemplateTag("包青天天包青天", colors[8]));
	list.add(new TemplateTag("包青天123456789123456789123456789123456789123456789", colors[9]));
	return list;
}x
1
public static List<TemplateTag> getTemplateTagList() {2
    List<TemplateTag> list = new ArrayList<>();3
    int colors[] = new int[10];4
    5
    for (int i = 0, j = colors.length; i < j; i++) {6
        colors[i] = 0xFF000000 + new Random().nextInt(0xFFFFFF);7
        //注意,Integer.MAX_VALUE= 0x7fffffff,超过这个值就是负数了,所以通过随机数获取int值时,传入的值要小于0x7fffffff8
    }9
    list.add(new TemplateTag("白乾涛", colors[0]));10
    list.add(new TemplateTag("tag", colors[1]));11
    list.add(new TemplateTag("标签 tag", colors[2]));12
    list.add(new TemplateTag("1", colors[3]));13
    list.add(new TemplateTag("颜色 " + colors[4], colors[4]));14
    list.add(new TemplateTag("包青天", colors[5]));15
    list.add(new TemplateTag("包青天包青天", colors[6]));16
    list.add(new TemplateTag("包青天包青天包青天包青天包青天包青天包青天", colors[7]));17
    list.add(new TemplateTag("包青天天包青天", colors[8]));18
    list.add(new TemplateTag("包青天123456789123456789123456789123456789123456789", colors[9]));19
    return list;20
}背景
tvContent.setBackground(getDrawable(mTemplateTag.color));
private GradientDrawable getDrawable(int bgColor) {
	GradientDrawable gradientDrawable = new GradientDrawable();
	gradientDrawable.setColor(bgColor);
	gradientDrawable.setCornerRadius(DisplayUtil.dip2px(ApplicationContext.getContext(), 4));
	return gradientDrawable;
}1
private GradientDrawable getDrawable(int bgColor) {2
    GradientDrawable gradientDrawable = new GradientDrawable();3
    gradientDrawable.setColor(bgColor);4
    gradientDrawable.setCornerRadius(DisplayUtil.dip2px(ApplicationContext.getContext(), 4));5
    return gradientDrawable;6
}FlowLayoutManager
/**
 * 描述:一种流式布局的LayoutManager<p>
 * 添加时间:2017/12/26 <p>
 * 开发者:<a href="http://www.cnblogs.com/baiqiantao">白乾涛</a><p>
 */
public class FlowLayoutManager extends RecyclerView.LayoutManager {
	
	private static final String TAG = FlowLayoutManager.class.getSimpleName();
	final FlowLayoutManager self = this;
	
	private int width, height;
	private int left, top, right;
	//最大容器的宽度
	private int usedMaxWidth;
	//竖直方向上的偏移量
	private int verticalScrollOffset = 0;
	
	public int getTotalHeight() {
		return totalHeight;
	}
	
	//计算显示的内容的高度
	private int totalHeight = 0;
	private Row row = new Row();
	private List<Row> lineRows = new ArrayList<>();
	
	//保存所有的Item的上下左右的偏移量信息
	private SparseArray<Rect> allItemFrames = new SparseArray<>();
	
	public FlowLayoutManager() {
		//设置主动测量规则,适应recyclerView高度为wrap_content
		setAutoMeasureEnabled(true);
	}
	
	//每个item的定义
	public class Item {
		int useHeight;
		View view;
		
		public void setRect(Rect rect) {
			this.rect = rect;
		}
		
		Rect rect;
		
		public Item(int useHeight, View view, Rect rect) {
			this.useHeight = useHeight;
			this.view = view;
			this.rect = rect;
		}
	}
	
	//行信息的定义
	public class Row {
		public void setCuTop(float cuTop) {
			this.cuTop = cuTop;
		}
		
		public void setMaxHeight(float maxHeight) {
			this.maxHeight = maxHeight;
		}
		
		//每一行的头部坐标
		float cuTop;
		//每一行需要占据的最大高度
		float maxHeight;
		//每一行存储的item
		List<Item> views = new ArrayList<>();
		
		public void addViews(Item view) {
			views.add(view);
		}
	}
	
	@Override
	public RecyclerView.LayoutParams generateDefaultLayoutParams() {
		return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
	}
	
	//该方法主要用来获取每一个item在屏幕上占据的位置
	@Override
	public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
		Log.d(TAG, "onLayoutChildren");
		if (getItemCount() == 0) {
			detachAndScrapAttachedViews(recycler);
			verticalScrollOffset = 0;
			return;
		}
		if (getChildCount() == 0 && state.isPreLayout()) {
			return;
		}
		//onLayoutChildren方法在RecyclerView 初始化时 会执行两遍
		detachAndScrapAttachedViews(recycler);
		if (getChildCount() == 0) {
			width = getWidth();
			height = getHeight();
			left = getPaddingLeft();
			right = getPaddingRight();
			top = getPaddingTop();
			usedMaxWidth = width - left - right;
		}
		totalHeight = 0;
		int cuLineTop = top;
		//当前行使用的宽度
		int cuLineWidth = 0;
		int itemLeft;
		int itemTop;
		int maxHeightItem = 0;
		row = new Row();
		lineRows.clear();
		allItemFrames.clear();
		removeAllViews();
		for (int i = 0; i < getItemCount(); i++) {
			Log.d(TAG, "index:" + i);
			View childAt = recycler.getViewForPosition(i);
			if (View.GONE == childAt.getVisibility()) {
				continue;
			}
			measureChildWithMargins(childAt, 0, 0);
			int childWidth = getDecoratedMeasuredWidth(childAt);
			int childHeight = getDecoratedMeasuredHeight(childAt);
			int childUseWidth = childWidth;
			int childUseHeight = childHeight;
			//如果加上当前的item还小于最大的宽度的话
			if (cuLineWidth + childUseWidth <= usedMaxWidth) {
				itemLeft = left + cuLineWidth;
				itemTop = cuLineTop;
				Rect frame = allItemFrames.get(i);
				if (frame == null) {
					frame = new Rect();
				}
				frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);
				allItemFrames.put(i, frame);
				cuLineWidth += childUseWidth;
				maxHeightItem = Math.max(maxHeightItem, childUseHeight);
				row.addViews(new Item(childUseHeight, childAt, frame));
				row.setCuTop(cuLineTop);
				row.setMaxHeight(maxHeightItem);
			} else {
				//换行
				formatAboveRow();
				cuLineTop += maxHeightItem;
				totalHeight += maxHeightItem;
				itemTop = cuLineTop;
				itemLeft = left;
				Rect frame = allItemFrames.get(i);
				if (frame == null) {
					frame = new Rect();
				}
				frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);
				allItemFrames.put(i, frame);
				cuLineWidth = childUseWidth;
				maxHeightItem = childUseHeight;
				row.addViews(new Item(childUseHeight, childAt, frame));
				row.setCuTop(cuLineTop);
				row.setMaxHeight(maxHeightItem);
			}
			//不要忘了最后一行进行刷新下布局
			if (i == getItemCount() - 1) {
				formatAboveRow();
				totalHeight += maxHeightItem;
			}
			
		}
		totalHeight = Math.max(totalHeight, getVerticalSpace());
		fillLayout(recycler, state);
	}
	
	//对出现在屏幕上的item进行展示,超出屏幕的item回收到缓存中
	private void fillLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
		if (state.isPreLayout()) { // 跳过preLayout,preLayout主要用于支持动画
			return;
		}
		
		// 当前scroll offset状态下的显示区域
		Rect displayFrame = new Rect(getPaddingLeft(), getPaddingTop() + verticalScrollOffset,
				getWidth() - getPaddingRight(), verticalScrollOffset + (getHeight() - getPaddingBottom()));
		
		//对所有的行信息进行遍历
		for (int j = 0; j < lineRows.size(); j++) {
			Row row = lineRows.get(j);
			float lineTop = row.cuTop;
			float lineBottom = lineTop + row.maxHeight;
			//如果该行在屏幕中,进行放置item
			if (lineTop < displayFrame.bottom && displayFrame.top < lineBottom) {
				List<Item> views = row.views;
				for (int i = 0; i < views.size(); i++) {
					View scrap = views.get(i).view;
					measureChildWithMargins(scrap, 0, 0);
					addView(scrap);
					Rect frame = views.get(i).rect;
					//将这个item布局出来
					layoutDecoratedWithMargins(scrap,
							frame.left,
							frame.top - verticalScrollOffset,
							frame.right,
							frame.bottom - verticalScrollOffset);
				}
			} else {
				//将不在屏幕中的item放到缓存中
				List<Item> views = row.views;
				for (int i = 0; i < views.size(); i++) {
					View scrap = views.get(i).view;
					removeAndRecycleView(scrap, recycler);
				}
			}
		}
	}
	
	/**
	 * 计算每一行没有居中的viewgroup,让居中显示
	 */
	private void formatAboveRow() {
		List<Item> views = row.views;
		for (int i = 0; i < views.size(); i++) {
			Item item = views.get(i);
			View view = item.view;
			int position = getPosition(view);
			//如果该item的位置不在该行中间位置的话,进行重新放置
			if (allItemFrames.get(position).top < row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2) {
				Rect frame = allItemFrames.get(position);
				if (frame == null) {
					frame = new Rect();
				}
				frame.set(allItemFrames.get(position).left, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2),
						allItemFrames.get(position).right, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2 + getDecoratedMeasuredHeight(view)));
				allItemFrames.put(position, frame);
				item.setRect(frame);
				views.set(i, item);
			}
		}
		row.views = views;
		lineRows.add(row);
		row = new Row();
	}
	
	/**
	 * 竖直方向需要滑动的条件
	 *
	 * @return
	 */
	@Override
	public boolean canScrollVertically() {
		return true;
	}
	
	//监听竖直方向滑动的偏移量
	@Override
	public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
	                              RecyclerView.State state) {
		
		Log.d("TAG", "totalHeight:" + totalHeight);
		//实际要滑动的距离
		int travel = dy;
		
		//如果滑动到最顶部
		if (verticalScrollOffset + dy < 0) {//限制滑动到顶部之后,不让继续向上滑动了
			travel = -verticalScrollOffset;//verticalScrollOffset=0
		} else if (verticalScrollOffset + dy > totalHeight - getVerticalSpace()) {//如果滑动到最底部
			travel = totalHeight - getVerticalSpace() - verticalScrollOffset;//verticalScrollOffset=totalHeight - getVerticalSpace()
		}
		
		//将竖直方向的偏移量+travel
		verticalScrollOffset += travel;
		
		// 平移容器内的item
		offsetChildrenVertical(-travel);
		fillLayout(recycler, state);
		return travel;
	}
	
	private int getVerticalSpace() {
		return self.getHeight() - self.getPaddingBottom() - self.getPaddingTop();
	}
	
	public int getHorizontalSpace() {
		return self.getWidth() - self.getPaddingLeft() - self.getPaddingRight();
	}
	
}280
280
1
/**2
 * 描述:一种流式布局的LayoutManager<p>3
 * 添加时间:2017/12/26 <p>4
 * 开发者:<a href="http://www.cnblogs.com/baiqiantao">白乾涛</a><p>5
 */6
public class FlowLayoutManager extends RecyclerView.LayoutManager {7
    8
    private static final String TAG = FlowLayoutManager.class.getSimpleName();9
    final FlowLayoutManager self = this;10
    11
    private int width, height;12
    private int left, top, right;13
    //最大容器的宽度14
    private int usedMaxWidth;15
    //竖直方向上的偏移量16
    private int verticalScrollOffset = 0;17
    18
    public int getTotalHeight() {19
        return totalHeight;20
    }21
    22
    //计算显示的内容的高度23
    private int totalHeight = 0;24
    private Row row = new Row();25
    private List<Row> lineRows = new ArrayList<>();26
    27
    //保存所有的Item的上下左右的偏移量信息28
    private SparseArray<Rect> allItemFrames = new SparseArray<>();29
    30
    public FlowLayoutManager() {31
        //设置主动测量规则,适应recyclerView高度为wrap_content32
        setAutoMeasureEnabled(true);33
    }34
    35
    //每个item的定义36
    public class Item {37
        int useHeight;38
        View view;39
        40
        public void setRect(Rect rect) {41
            this.rect = rect;42
        }43
        44
        Rect rect;45
        46
        public Item(int useHeight, View view, Rect rect) {47
            this.useHeight = useHeight;48
            this.view = view;49
            this.rect = rect;50
        }51
    }52
    53
    //行信息的定义54
    public class Row {55
        public void setCuTop(float cuTop) {56
            this.cuTop = cuTop;57
        }58
        59
        public void setMaxHeight(float maxHeight) {60
            this.maxHeight = maxHeight;61
        }62
        63
        //每一行的头部坐标64
        float cuTop;65
        //每一行需要占据的最大高度66
        float maxHeight;67
        //每一行存储的item68
        List<Item> views = new ArrayList<>();69
        70
        public void addViews(Item view) {71
            views.add(view);72
        }73
    }74
    75
    76
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {77
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);78
    }79
    80
    //该方法主要用来获取每一个item在屏幕上占据的位置81
    82
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {83
        Log.d(TAG, "onLayoutChildren");84
        if (getItemCount() == 0) {85
            detachAndScrapAttachedViews(recycler);86
            verticalScrollOffset = 0;87
            return;88
        }89
        if (getChildCount() == 0 && state.isPreLayout()) {90
            return;91
        }92
        //onLayoutChildren方法在RecyclerView 初始化时 会执行两遍93
        detachAndScrapAttachedViews(recycler);94
        if (getChildCount() == 0) {95
            width = getWidth();96
            height = getHeight();97
            left = getPaddingLeft();98
            right = getPaddingRight();99
            top = getPaddingTop();100
            usedMaxWidth = width - left - right;101
        }102
        totalHeight = 0;103
        int cuLineTop = top;104
        //当前行使用的宽度105
        int cuLineWidth = 0;106
        int itemLeft;107
        int itemTop;108
        int maxHeightItem = 0;109
        row = new Row();110
        lineRows.clear();111
        allItemFrames.clear();112
        removeAllViews();113
        for (int i = 0; i < getItemCount(); i++) {114
            Log.d(TAG, "index:" + i);115
            View childAt = recycler.getViewForPosition(i);116
            if (View.GONE == childAt.getVisibility()) {117
                continue;118
            }119
            measureChildWithMargins(childAt, 0, 0);120
            int childWidth = getDecoratedMeasuredWidth(childAt);121
            int childHeight = getDecoratedMeasuredHeight(childAt);122
            int childUseWidth = childWidth;123
            int childUseHeight = childHeight;124
            //如果加上当前的item还小于最大的宽度的话125
            if (cuLineWidth + childUseWidth <= usedMaxWidth) {126
                itemLeft = left + cuLineWidth;127
                itemTop = cuLineTop;128
                Rect frame = allItemFrames.get(i);129
                if (frame == null) {130
                    frame = new Rect();131
                }132
                frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);133
                allItemFrames.put(i, frame);134
                cuLineWidth += childUseWidth;135
                maxHeightItem = Math.max(maxHeightItem, childUseHeight);136
                row.addViews(new Item(childUseHeight, childAt, frame));137
                row.setCuTop(cuLineTop);138
                row.setMaxHeight(maxHeightItem);139
            } else {140
                //换行141
                formatAboveRow();142
                cuLineTop += maxHeightItem;143
                totalHeight += maxHeightItem;144
                itemTop = cuLineTop;145
                itemLeft = left;146
                Rect frame = allItemFrames.get(i);147
                if (frame == null) {148
                    frame = new Rect();149
                }150
                frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);151
                allItemFrames.put(i, frame);152
                cuLineWidth = childUseWidth;153
                maxHeightItem = childUseHeight;154
                row.addViews(new Item(childUseHeight, childAt, frame));155
                row.setCuTop(cuLineTop);156
                row.setMaxHeight(maxHeightItem);157
            }158
            //不要忘了最后一行进行刷新下布局159
            if (i == getItemCount() - 1) {160
                formatAboveRow();161
                totalHeight += maxHeightItem;162
            }163
            164
        }165
        totalHeight = Math.max(totalHeight, getVerticalSpace());166
        fillLayout(recycler, state);167
    }168
    169
    //对出现在屏幕上的item进行展示,超出屏幕的item回收到缓存中170
    private void fillLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {171
        if (state.isPreLayout()) { // 跳过preLayout,preLayout主要用于支持动画172
            return;173
        }174
        175
        // 当前scroll offset状态下的显示区域176
        Rect displayFrame = new Rect(getPaddingLeft(), getPaddingTop() + verticalScrollOffset,177
                getWidth() - getPaddingRight(), verticalScrollOffset + (getHeight() - getPaddingBottom()));178
        179
        //对所有的行信息进行遍历180
        for (int j = 0; j < lineRows.size(); j++) {181
            Row row = lineRows.get(j);182
            float lineTop = row.cuTop;183
            float lineBottom = lineTop + row.maxHeight;184
            //如果该行在屏幕中,进行放置item185
            if (lineTop < displayFrame.bottom && displayFrame.top < lineBottom) {186
                List<Item> views = row.views;187
                for (int i = 0; i < views.size(); i++) {188
                    View scrap = views.get(i).view;189
                    measureChildWithMargins(scrap, 0, 0);190
                    addView(scrap);191
                    Rect frame = views.get(i).rect;192
                    //将这个item布局出来193
                    layoutDecoratedWithMargins(scrap,194
                            frame.left,195
                            frame.top - verticalScrollOffset,196
                            frame.right,197
                            frame.bottom - verticalScrollOffset);198
                }199
            } else {200
                //将不在屏幕中的item放到缓存中201
                List<Item> views = row.views;202
                for (int i = 0; i < views.size(); i++) {203
                    View scrap = views.get(i).view;204
                    removeAndRecycleView(scrap, recycler);205
                }206
            }207
        }208
    }209
    210
    /**211
     * 计算每一行没有居中的viewgroup,让居中显示212
     */213
    private void formatAboveRow() {214
        List<Item> views = row.views;215
        for (int i = 0; i < views.size(); i++) {216
            Item item = views.get(i);217
            View view = item.view;218
            int position = getPosition(view);219
            //如果该item的位置不在该行中间位置的话,进行重新放置220
            if (allItemFrames.get(position).top < row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2) {221
                Rect frame = allItemFrames.get(position);222
                if (frame == null) {223
                    frame = new Rect();224
                }225
                frame.set(allItemFrames.get(position).left, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2),226
                        allItemFrames.get(position).right, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2 + getDecoratedMeasuredHeight(view)));227
                allItemFrames.put(position, frame);228
                item.setRect(frame);229
                views.set(i, item);230
            }231
        }232
        row.views = views;233
        lineRows.add(row);234
        row = new Row();235
    }236
    237
    /**238
     * 竖直方向需要滑动的条件239
     *240
     * @return241
     */242
    243
    public boolean canScrollVertically() {244
        return true;245
    }246
    247
    //监听竖直方向滑动的偏移量248
    249
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,250
                                  RecyclerView.State state) {251
        252
        Log.d("TAG", "totalHeight:" + totalHeight);253
        //实际要滑动的距离254
        int travel = dy;255
        256
        //如果滑动到最顶部257
        if (verticalScrollOffset + dy < 0) {//限制滑动到顶部之后,不让继续向上滑动了258
            travel = -verticalScrollOffset;//verticalScrollOffset=0259
        } else if (verticalScrollOffset + dy > totalHeight - getVerticalSpace()) {//如果滑动到最底部260
            travel = totalHeight - getVerticalSpace() - verticalScrollOffset;//verticalScrollOffset=totalHeight - getVerticalSpace()261
        }262
        263
        //将竖直方向的偏移量+travel264
        verticalScrollOffset += travel;265
        266
        // 平移容器内的item267
        offsetChildrenVertical(-travel);268
        fillLayout(recycler, state);269
        return travel;270
    }271
    272
    private int getVerticalSpace() {273
        return self.getHeight() - self.getPaddingBottom() - self.getPaddingTop();274
    }275
    276
    public int getHorizontalSpace() {277
        return self.getWidth() - self.getPaddingLeft() - self.getPaddingRight();278
    }279
    280
}2017-12-27
 
        