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.mzph.cn/news/612561.shtml

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

相关文章

MyBatis源码分析(六):数据源模块

1. 概述 本文&#xff0c;我们来分享 MyBatis 的数据源模块&#xff0c;对应 datasource 包。如下图所示&#xff1a; ​ 在 MyBatis源码分析&#xff08;二&#xff09;&#xff1a;项目结构 中&#xff0c;简单介绍了这个模块如下&#xff1a; 数据源是实际开发中常用的组件…

Python Jinja2:强大易用的模板引擎

Jinja2是一个基于Python的模板引擎&#xff0c;它的功能类似于PHP的smarty&#xff0c;J2ee的Freemarker和velocity。 模板引擎的应用场景非常广泛&#xff0c;它可以将动态数据与静态模板结合&#xff0c;提高发效率和代码可维护性。 我们平时接触各类网页&#xff0c;邮件通…

RIS 辅助无线网络:基于模型、启发式和机器学习の优化方法

目录 abstractintroduction相关研究BACKGROUND AND PROBLEM FORMULATIONS FOR OPTIMIZING RIS-AIDED WIRELESS NETWORKSA 优化RIS-AIDED无线网络的背景和问题公式RIS操作原则&#xff1a;RIS控制&#xff1a;RIS部署 B 总速率/容量最大化C 功率最小化D 能源效率最大化E 用户公平…

Opencv实验合集——实验七:二维码和条形码匹配

1.概念 二维码&#xff08;QR码&#xff09; 概念&#xff1a; 二维码是一种矩阵式的二维条码&#xff0c;由黑白方块组成&#xff0c;可以存储大量的信息&#xff0c;包括文本、链接、数字等。QR码的编码方式是在矩阵中通过不同的黑白方块组合表示不同的信息。 特点&#xf…

李沐-《动手学深度学习》--02-目标检测

一 、目标检测算法 1. R-CNN a . 算法步骤 使用启发式搜索算法来选择锚框&#xff08;选出多个锚框大小可能不一&#xff0c;需要使用Rol pooling&#xff09;使用预训练好的模型&#xff08;去掉分类层&#xff09;对每个锚框进行特征抽取&#xff08;如VGG,AlexNet…)训练…

招投标系统是Electron的纯内网编辑Office Word,可以设置部分区域可编辑,其他的地方不能编辑吗?

问题&#xff1a; 我们是招投标系统的开发公司&#xff0c;框架是用的Electron&#xff0c;需要在纯内网的环境下编辑Office Word&#xff0c;可以设置部分区域可编辑&#xff0c;其他的地方不能编辑吗&#xff08;如下红框位置&#xff09;并且在用户忘记填写一些区域的时候做…

爬虫技术的法律风险与规避方法,你必须知道!

在互联网时代&#xff0c;信息的获取变得异常重要。爬虫技术&#xff0c;一种可以自动提取网页数据的工具&#xff0c;应运而生。然而&#xff0c;随着爬虫技术的广泛应用&#xff0c;一个问题愈发凸显&#xff1a;爬虫究竟是合法的工具&#xff0c;还是潜藏着法律风险的数字探…

K8S 存储卷

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

React 基本使用

create-react-app 创建 react 项目的脚手架。 React 基本用法 jsx 语法 变量、表达式 import React from react;class JSXBaseDemo extends React.Component {constructor(props) {super(props);this.state {name: 章三};}render() {// 获取变量 插值const pElem <p&…

如何用 Python 实现一个 “系统声音” 的实时律动挂件

前言 应该是三年前&#xff0c;我用 Esp8266 和 ws2812 实现了一个音乐律动灯带。就是电脑播放音乐时&#xff0c;灯带会随着系统内部音乐播放的频率而闪动不同色彩的灯珠。而当时用来监听系统声音的工具是一个博主提供的&#xff0c;除了实时采集声音外还通过 UDP 传递数据到…

玉米浸泡液植酸吸附树脂

植酸为环己六醇六磷酸&#xff0c;分子量为660&#xff0c;植酸钠为环己六醇六磷酸钠&#xff08;Na12C6H6O24P6&#xff09;分子量为924。 植酸用途&#xff1a;在食品工业中植酸钠可用作食品添设剂&#xff0c;菲丁&#xff08;植酸钙&#xff09;可用于生产肌醇&#xff0c…

vscode+markdown+plantuml+html

markdown-preview-enhanced plantuml配置 生成html侧边栏 --- html:toc: truetoc_depth: 6toc_float: truecollapsed: falsesmooth_scroll: true ---

多测师肖sir___接口自动化测试框架(python+request+unittest+ddt)讲解版

pythonrequestunittestddt 一、接口自动化测试框架&#xff08;pythonrequestunittestddtj 7个包&#xff09;讲解 首先我们新建一个新项目:名称zdh 二、在一个项目中&#xff1a;新建7个包 第一个包conf包用来填写配置参数、地址等 第二个包data 包用来存放测试用例的表格 第…

视频转gif的在线转换怎么操作?告别繁琐,轻松搞定

视频转gif的在线转换怎么操作&#xff1f;在当今社交媒体盛行的时代&#xff0c;GIF动图已经成为了我们表达自我、分享生活的重要方式。但是&#xff0c;很多小伙伴可能还在为如何将心爱的视频片段转为GIF而烦恼。今天&#xff0c;我们就来一起学习如何将视频轻松转换为GIF的在…

数据库编程大赛冠军:郑凌云:0.67秒通过百万级数据评测!SQL代码惊现神之一手!

12月27日&#xff0c;NineData和云数据库技术社区主办&#xff0c;华为云、火山引擎、开源中国、云和恩墨、TDengine、云猿生数据、DORIS、ITPUB等协办单位和媒体&#xff0c;共同举办了本次《数据库编程大赛》。大赛题目「用一条SQL给出扑克牌24点的计算表达式」。 以下冠军选…

JS 高频面试题

JS 的数据类型有哪些&#xff0c;有什么区别 基本数据类型&#xff08;Undefined、Null、Boolean、Number、String、Symbol&#xff09; 引用数据类型&#xff08;对象、数组和函数&#xff09; 区别&#xff1a; 原始数据类型直接存储在栈&#xff08;stack&#xff09;中的简…

【亲测有效】Win11 卸载MySQL5.7以及安装MySQL8.0.35

目录 一、卸载原来本地的mysql5.7 1.mysql服务部分 1.1停止mysql服务 1.2删除mysql服务 2.卸载 MySQL程序 3.残余文件的清理 3.1删除mysql安装的目录 3.2删除mysql数据存放的目录 3.3删除mysql自定义目录 4.清理注册表 5.删除环境变量配置 二、安装mysql8.0.35 1.…

【Spring Boot】SpringMVC入门

1.什么是springMVC MVC就是把一个项目分成了三部分&#xff1a; MVC是一种思想。Spring进行了实现,称为Spring MVC。SpringBoot是创建SpringMVC项目的一种方式而已。springMVC对于MVC做出了一些改变&#xff1a; 当前阶段,MVC的概念又发生了一些变化,后端开发人员不涉及前端页…

众和策略股市行情分析:了解散户的典型特征

散户特色如下&#xff1a; 1、出资规模小&#xff1a;散户的出资规模一般比较小&#xff0c;资金量有限&#xff0c;无法对商场发生显著的影响。资金量较小的一起&#xff0c;其对危险的承受能力也相对较弱。 2、缺少危险意识&#xff1a;散户往往缺少危险意识&#xff0c;没…

2024年网络工程师10大必备软件,最新安装包分享

功夫再高&#xff0c;也怕菜刀。作为网络工程师&#xff0c;我们手中的菜刀是什么&#xff1f; 以下10大网工必备软件都已整理好安装包&#xff0c;需要的朋友可以在文末获取。 1、Cisco Packet Tracer&#xff08;思科模拟器&#xff09; Cisco Packet Tracer 是由Cisco公司发…