Android通过Recyclerview实现流式布局自适应列数及高度

在这里插入图片描述

调用 FlowAdapter 跟普通recyclerview一样使用

RecyclerView rvLayout = holder.getView(R.id.spe_tag_layout);
FlowAdapter rvAdapter = new FlowAdapter();
FlowLayoutManager flowLayoutManager = new FlowLayoutManager();
rvLayout.setLayoutManager(flowLayoutManager);
rvLayout.setAdapter(rvAdapter);

/*** date:2024/1/10* author:wsm* describe:一种流式布局的LayoutManager*/
public class FlowLayoutManager extends RecyclerView.LayoutManager {private static final String TAG = FlowLayoutManager.class.getSimpleName();final FlowLayoutManager self = this;protected int width, height;private int left, top, right;//最大容器的宽度private int usedMaxWidth;//竖直方向上的偏移量private int verticalScrollOffset = 0;public int getTotalHeight() {return totalHeight;}//计算显示的内容的高度protected 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_contentsetAutoMeasureEnabled(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;//每一行存储的itemList<Item> views = new ArrayList<>();public void addViews(Item view) {views.add(view);}}@Overridepublic RecyclerView.LayoutParams generateDefaultLayoutParams() {return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);}//该方法主要用来获取每一个item在屏幕上占据的位置@Overridepublic void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {Log.d(TAG, "onLayoutChildren");totalHeight = 0;int cuLineTop = top;//当前行使用的宽度int cuLineWidth = 0;int itemLeft;int itemTop;int maxHeightItem = 0;row = new Row();lineRows.clear();allItemFrames.clear();removeAllViews();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;}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());Log.d(TAG, "onLayoutChildren totalHeight:" + totalHeight);fillLayout(recycler, state);}//对出现在屏幕上的item进行展示,超出屏幕的item回收到缓存中private void fillLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {if (state.isPreLayout() || getItemCount() == 0) { // 跳过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*/@Overridepublic boolean canScrollVertically() {return true;}//监听竖直方向滑动的偏移量@Overridepublic 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()}//将竖直方向的偏移量+travelverticalScrollOffset += travel;// 平移容器内的itemoffsetChildrenVertical(-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();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/338690.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS 6 制作openssl 1.1.1w rpm包 —— 筑梦之路

参考资料&#xff1a; CentOS 7 制作openssl 1.1.1w 版本rpm包 —— 筑梦之路_centos7 openssl 1.1.1 rpm包-CSDN博客 直接上spec文件如下&#xff1a; Name: openssl Version: 1.1.1w Release: 1%{?dist} Summary: Utilities from the general purpose cryptography li…

Kubernetes/k8s的存储卷/数据卷

k8s的存储卷/数据卷 容器内的目录和宿主机的目录挂载 容器在系统上的生命周期是短暂的&#xff0c;delete&#xff0c;k8s用控制创建的pod&#xff0c;delete相当于重启&#xff0c;容器的状态也会回复到初始状态 一旦回到初始状态&#xff0c;所有的后天编辑的文件都会消失…

C++从零基础到入门(1)

目录 一、输入输出 (iostream库) 1.标准输出流cout 2.标准输入流cin 3.标准库iostream &#xff08;1&#xff09;iostream中的窄字符&#xff08;char&#xff09; &#xff08;2&#xff09;iostream中的 宽字符&#xff08;wchar_t&#xff09; 二、变量与数据类型 …

基于DNA的密码学和隐写术综述

摘要 本文全面调研了不同的脱氧核糖核酸(DNA)-基于密码学和隐写术技术。基于DNA的密码学是一个新兴领域,利用DNA分子的大规模并行性和巨大的存储容量来编码和解码信息。近年来,由于其相对传统密码学方法的潜在优势,如高存储容量、低错误率和对环境因素的抗性,该领域引起…

Geotools-PG空间库(Crud,属性查询,空间查询)

建立连接 经过测试&#xff0c;这套连接逻辑除了支持纯PG以外&#xff0c;也支持人大金仓&#xff0c;凡是套壳PG的都可以尝试一下。我这里的测试环境是Geosence创建的pg SDE&#xff0c;数据库选用的是人大金仓。 /*** 获取数据库连接资源** param connectConfig* return* {…

数据结构实验4:链表的基本操作

目录 一、实验目的 二、实验原理 1. 节点 2. 指针 3.链表的类型 3.1 单向链表 3.2 双向链表 3.3 单向循环链表 3.4 双向循环链表 4. 单链表的插入 4.1 头插法 4.2 尾插法 4.3 在指定位置插入元素 5. 单链表的删除 5.1 删除指定数值的节点 5.2 删除指定位置的节点 …

java基础之Java8新特性-Optional

目录 1.简介 2.Optional类常用方法 3.示例代码 4.示例代码仓库地址 1.简介 Java 8引入了一个重要的新特性&#xff0c;即Optional类。Optional类是为了解决空指针异常而设计的。 在Java中&#xff0c;当我们尝试访问一个空对象的属性或调用其方法时&#xff0c;很容易抛出…

如何创建自己的小程序?零编程一键创建实战指南

当今瞬息万变的数字世界中&#xff0c;拥有一个属于自己的小程序已成为企业与个人展示、服务和互动的重要途径。无需编码知识&#xff0c;通过便捷的云端可视化平台&#xff0c;也可以轻松创建一款符合自身需求且功能丰富的小程序。下面给大家分享如何创建自己的小程序。 1、选…

QT开发 2024最新版本优雅的使用vscode开发QT

▬▬▬▬▬▶VS开发QT◀▬▬▬▬▬ &#x1f384;先看效果 &#x1f384;编辑环境变量 如图添加环境变量&#xff01;&#xff01;&#xff01; 东西全在QT的安装目录&#xff01;&#xff01;&#xff01; 找到的按照我的教程再装一次&#xff01;&#xff01;&#xff01; 点…

OpenCV-21方盒滤波和均值滤波

一、方和滤波 使用API --- boxFiter(src, ddepth, ksize[,dst[,anchor[, normalize[, borderType]]]])方盒滤波 方盒滤波的卷积核如下所示&#xff1a; --- normalize Ture时&#xff0c; a 1 / &#xff08;W*H&#xff09;滤波器的宽高 --- normalize False时&#xff…

(超详细)YOLOV5改进-添加SimAM注意力机制

1、在yolov5/models下面新建一个SimAM.py文件&#xff0c;在里面放入下面的代码 代码如下&#xff1a; import torch import torch.nn as nnclass SimAM(torch.nn.Module):def __init__(self, e_lambda1e-4):super(SimAM, self).__init__()self.activaton nn.Sigmoid()self…

回归预测 | Matlab基于SO-LSTM蛇群算法优化长短期记忆神经网络的数据多输入单输出回归预测

回归预测 | Matlab基于SO-LSTM蛇群算法优化长短期记忆神经网络的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SO-LSTM蛇群算法优化长短期记忆神经网络的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于SO-LSTM蛇群算法优化…