码迷,mamicode.com
首页 > 其他好文 > 详细

RecyclerView LayoutManager 流式布局

时间:2017-12-27 11:47:04      阅读:127      评论:0      收藏:0      [点我收藏+]

标签:void   radius   private   man   ram   line   end   opera   log   

使用

技术分享图片
.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
    @Override
76
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
77
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
78
    }
79
    
80
    //该方法主要用来获取每一个item在屏幕上占据的位置
81
    @Override
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
    @Override
243
    public boolean canScrollVertically() {
244
        return true;
245
    }
246
    
247
    //监听竖直方向滑动的偏移量
248
    @Override
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

RecyclerView LayoutManager 流式布局

标签:void   radius   private   man   ram   line   end   opera   log   

原文地址:https://www.cnblogs.com/baiqiantao/p/8124560.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!