使用
.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值时,传入的值要小于0x7fffffff
8
}
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_content
32
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
//每一行存储的item
68
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
//如果该行在屏幕中,进行放置item
185
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
* @return
241
*/
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=0
259
} else if (verticalScrollOffset + dy > totalHeight - getVerticalSpace()) {//如果滑动到最底部
260
travel = totalHeight - getVerticalSpace() - verticalScrollOffset;//verticalScrollOffset=totalHeight - getVerticalSpace()
261
}
262
263
//将竖直方向的偏移量+travel
264
verticalScrollOffset += travel;
265
266
// 平移容器内的item
267
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