当前位置:   article > 正文

android RecyclerView实现流失布局增加自定义布局管理器(FlowLayoutManager)_android flowlayoutmanager

android flowlayoutmanager

1.效果如如下

2.自定义的布局管理器FlowLayoutManager

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

3.主界面

  1. package com.zw.flowlayoutdemo;
  2. import android.os.Handler;
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.os.Bundle;
  5. import android.support.v7.widget.RecyclerView;
  6. import android.text.Editable;
  7. import android.text.TextUtils;
  8. import android.text.TextWatcher;
  9. import android.view.View;
  10. import android.widget.Button;
  11. import android.widget.EditText;
  12. import android.widget.Toast;
  13. import java.util.ArrayList;
  14. import java.util.List;
  15. import java.util.regex.Matcher;
  16. import java.util.regex.Pattern;
  17. import java.util.regex.PatternSyntaxException;
  18. public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  19. private RecyclerView mRecyclerView;
  20. private EditText mEditText;
  21. private Button mBtnAdd;
  22. private MyAdapter myAdapter;
  23. private List<String>mList=new ArrayList<>();
  24. @Override
  25. protected void onCreate(Bundle savedInstanceState) {
  26. super.onCreate(savedInstanceState);
  27. setContentView(R.layout.activity_main);
  28. initView();
  29. initData();
  30. }
  31. private void initData() {
  32. List<String>list=new ArrayList<>();
  33. String [] str=new String[]{"我喜欢你","你可以的","我们为什么不换一种方式","我爱你哟","你用了什么东西"};
  34. for (int i = 0; i <5 ; i++) {
  35. list.add(str[i]);
  36. }
  37. mList.clear();
  38. mList.addAll(list);
  39. myAdapter.notifyDataSetChanged();
  40. }
  41. private void initView() {
  42. mRecyclerView=findViewById(R.id.rv_main);
  43. mBtnAdd=findViewById(R.id.btn_add);
  44. mBtnAdd.setOnClickListener(this);
  45. mEditText=findViewById(R.id.editText);
  46. myAdapter=new MyAdapter(this,mList);
  47. FlowLayoutManager flowLayoutManager=new FlowLayoutManager();
  48. mRecyclerView.addItemDecoration(new SpaceItemDecoration(10));
  49. mRecyclerView.setLayoutManager(flowLayoutManager);
  50. mRecyclerView.setAdapter(myAdapter);
  51. mEditText.addTextChangedListener(new TextWatcher() {
  52. @Override
  53. public void beforeTextChanged(CharSequence s, int start, int count, int after) {
  54. }
  55. @Override
  56. public void onTextChanged(CharSequence s, int start, int before, int count) {
  57. }
  58. @Override
  59. public void afterTextChanged(Editable s) {
  60. checkName(s,mEditText);
  61. }
  62. });
  63. }
  64. @Override
  65. public void onClick(View v) {
  66. switch (v.getId()){
  67. case R.id.btn_add:
  68. addItem();
  69. break;
  70. }
  71. }
  72. private void addItem() {
  73. String txt=mEditText.getText().toString();
  74. if (txt==null || txt.length()==0 || txt.equals("")) {
  75. return;
  76. } else {
  77. mRecyclerView.setVisibility(View.VISIBLE);
  78. //是否有相同的元素
  79. if (mList.contains(txt)) {
  80. Toast.makeText(this, "已存在相同的词", Toast.LENGTH_LONG).show();
  81. return;
  82. } else {
  83. mList.add(txt);
  84. }
  85. myAdapter.notifyDataSetChanged();
  86. new Handler().postDelayed(new Runnable() {
  87. @Override
  88. public void run() {
  89. mRecyclerView.scrollToPosition(myAdapter.getItemCount() - 1);
  90. }
  91. }, 2000);
  92. mEditText.setText("");
  93. }
  94. }
  95. /**
  96. * 检查输入的名称的合法性
  97. *
  98. * @param editable
  99. * @param editText
  100. */
  101. private void checkName(Editable editable, EditText editText) {
  102. String inputStr = editable.toString().trim();
  103. String str = stringFilter(inputStr);
  104. if (!inputStr.equals(str)) {
  105. editText.setText(str);
  106. editText.setSelection(editText.getText().toString().length());
  107. return;
  108. }
  109. int length = getTextLength(inputStr);
  110. if (length > 20) {
  111. String newStr = getStringWithSubLength(inputStr, 20);
  112. editText.setText(newStr);
  113. editText.setSelection(editText.getText().toString().length());
  114. }
  115. }
  116. /**
  117. * 只允许字母、汉字
  118. * @param str
  119. * @return
  120. * @throws PatternSyntaxException
  121. */
  122. public String stringFilter(String str) throws PatternSyntaxException {
  123. // 只允许字母、汉字
  124. String regEx = "[^a-zA-Z\\u4E00-\\u9FA5]";
  125. Pattern p = Pattern.compile(regEx);
  126. Matcher m = p.matcher(str);
  127. return m.replaceAll("").trim();
  128. }
  129. /**
  130. * 获取字符数量 汉字占2个,英文占一个
  131. *
  132. * @param text
  133. * @return
  134. */
  135. public int getTextLength(String text) {
  136. int length = 0;
  137. for (int i = 0; i < text.length(); i++) {
  138. if (text.charAt(i) > 255) {
  139. length += 2;
  140. } else {
  141. length++;
  142. }
  143. }
  144. return length;
  145. }
  146. public String getStringWithSubLength(String source, int maxCharLength) {
  147. if (TextUtils.isEmpty(source)) {
  148. return "";
  149. }
  150. if (getTextLength(source) <= maxCharLength) {
  151. return source;
  152. }
  153. char[] childrens = source.toCharArray();
  154. for (int i = 0; i < childrens.length; i++) {
  155. StringBuilder builder = new StringBuilder();
  156. for (int j = 0; j <= i; j++) {
  157. builder.append(childrens[j]);
  158. }
  159. if (getTextLength(builder.toString()) > maxCharLength) {
  160. return builder.replace(builder.length() - 1, builder.length(), "").toString();
  161. }
  162. }
  163. return "";
  164. }
  165. }

4.参考资料:https://github.com/xiangcman/LayoutManager-FlowLayout/blob/master/library/src/main/java/com/library/flowlayout/FlowLayoutManager.java

5.源码地址:https://download.csdn.net/download/zhang106209/11649507

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/174985
推荐阅读
相关标签
  

闽ICP备14008679号