实现 GridLayoutManger 和 StaggeredGridLayoutManager 混排的工具类

news2024/9/20 18:33:31

序言

最近项目中要实现瀑布流的混排,于是写了一些工具类来实现。使用了这个工具类,可以处理混排,可以处理间距。都集成在一个接口中。

最后效果类似这样。

在这里插入图片描述

工具类

GridItemUI

下面的这个类是用来实现在GridLayouManger中混排的。

package com.trs.baseapp.news.list.ui.staggered;

import android.graphics.Rect;
import android.util.Pair;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.trs.library.callback.action.TRSFunction;


/**
 * <pre>
 * Created by zhuguohui
 * Date: 2024/8/23
 * Time: 10:21
 * Desc:用来实现GridItem混排的接口。
 *
 * 用法如下:
 * <code>
 *       int spanCount = 3;
 *         GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), spanCount);
 *         GridItemUI.setSpanSizeLookup(gridLayoutManager,position->adapter.getItems().get(position));
 *         GridItemUI.addItemDecoration(recyclerView, spanCount, position -> adapter.getItems().get(position));
 * </code>
 * </pre>
 */
public interface GridItemUI {
    /**
     * 获取item之间的水平间隔 horizontal
     *
     * @return
     */
    int getItemHorizontalSpaceSize();

    /**
     * 获取Item 之间垂直方向的间隔
     *
     * @return
     */
    int getItemVerticalSpaceSize();


    static void setSpanSizeLookup(GridLayoutManager gridLayoutManager, TRSFunction<Integer, Object> findAdapterContentFunction) {
        gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                Object o = findAdapterContentFunction.call(position);
                if (o instanceof GridItemUI) {
                    return 1;
                }
                return gridLayoutManager.getSpanCount();
            }
        });
    }

    static void addItemDecoration(RecyclerView recyclerView, int spanCount, TRSFunction<Integer, Object> findAdapterContentFunction) {
        recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
                int position = parent.getChildAdapterPosition(view);
                Object o = findAdapterContentFunction.call(position);
                if (o instanceof GridItemUI) {
                    Pair<Boolean, Boolean> pair = getIsFirstLineAndIsLineFirstItem(position);
                    GridItemUI itemUI = (GridItemUI) o;
                    int hs = itemUI.getItemHorizontalSpaceSize();
                    int vs = itemUI.getItemVerticalSpaceSize();
                    outRect.left = pair.second ? hs : 0;
                    outRect.right = hs;
                    outRect.top = pair.first ? vs : 0;
                    outRect.bottom = vs;
                } else {
                    super.getItemOffsets(outRect, view, parent, state);
                }
            }

            private Pair<Boolean, Boolean> getIsFirstLineAndIsLineFirstItem(int position) {
                int beforeItemCount = 0;
                int checkPosition = position;
                do {
                    checkPosition--;
                    if (checkPosition < 0) {
                        break;
                    }
                    Object o = findAdapterContentFunction.call(checkPosition);
                    if (!(o instanceof GridItemUI)) {
                        break;
                    }
                    beforeItemCount++;

                } while (true);

                boolean lineFirst = beforeItemCount % spanCount == 0;
                boolean firstLine = beforeItemCount < spanCount;
                return new Pair<>(firstLine, lineFirst);
            }
        });
    }

}

用法


   int spanCount = 3;
          GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), spanCount);
          //以下代码实现混排
          GridItemUI. setSpanSizeLookup(gridLayoutManager,position->adapter. getItems().get(position));
          //以下代码实现间距
          GridItemUI. addItemDecoration(recyclerView, spanCount, position -> adapter. getItems().get(position));
          

StaggeredUI

package com.trs.baseapp.news.list.ui.staggered;

import android.graphics.Rect;
import android.util.Pair;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;

import com.trs.library.callback.action.TRSFunction;

/**
 * <pre>
 * Created by zhuguohui
 * Date: 2024/8/28
 * Time: 11:24
 * Desc:实现了该接口,表示当前UI是为了瀑布流设计的
 * </pre>
 */
public interface StaggeredUI {

    /**
     * 用来查找adapter中对应position数据的接口
     */
    interface FindAdapterContentFunction{
        Object call(int position);
    }
    static void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder,FindAdapterContentFunction findAdapterContentFunction) {

        int adapterPosition = holder.getAdapterPosition();
        Object o = findAdapterContentFunction.call(adapterPosition);
        StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
        if (o instanceof StaggeredUI) {
            params.setFullSpan(false);
        } else {
            params.setFullSpan(true);
        }


    }

    static void addItemDecoration(RecyclerView recyclerView, int spanCount, FindAdapterContentFunction findAdapterContentFunction) {
        recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
                int position = parent.getChildAdapterPosition(view);
                Object o = findAdapterContentFunction.call(position);

                if (o instanceof StaggeredUI itemUI) {
                    StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
                    int hs = itemUI.getItemHorizontalSpaceSize();
                    int vs = itemUI.getItemVerticalSpaceSize();
                    outRect.left = lp.getSpanIndex() == 0 ? hs : 0;
                    outRect.right = hs;
                    outRect.top = isFirstLine(position) ? vs : 0;
                    outRect.bottom = vs;
                } else {
                    super.getItemOffsets(outRect, view, parent, state);
                }
            }

            private boolean isFirstLine(int position) {
                int beforeItemCount = 0;
                int checkPosition = position;
                do {
                    checkPosition--;
                    if (checkPosition < 0) {
                        break;
                    }
                    Object o = findAdapterContentFunction.call(checkPosition);
                    if (!(o instanceof StaggeredUI)) {
                        break;
                    }
                    beforeItemCount++;

                } while (true);


                boolean firstLine = beforeItemCount < spanCount;
                return firstLine;
            }
        });
    }

    /**
     * 返回垂直间距
     * @return
     */
    int getItemVerticalSpaceSize();

    /**
     * 返回水平间距
     * @return
     */
    int getItemHorizontalSpaceSize();
}


使用

1.在混排的数据中,把要瀑布流显示的数据实现接口

默认所有数据都不是瀑布流,只有实现了StaggeredUI 接口的数据才是瀑布流

在这里插入图片描述

2.使用接口的静态方法设置间距

在这里插入图片描述

3.重写adapter的 onViewAttachedToWindow 方法

直接调用StaggeredUI的同名静态方法就行了

在这里插入图片描述

4.布局layout_width使用 match_parent

如果要保持一定比例使用约束布局就可以了。

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

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

相关文章

硬件-PCB-正片(常用默认)和负片

文章目录 问题&#xff1a;什么是PCB的正片和负片&#xff1f;1.正片设计默认是无铜的&#xff08;常用&#xff09;2.负片设计是默认有铜的3.网友评论道友&#xff1a;我们的对手从来不是别人&#xff0c;而是自己。如果有什么必须战胜&#xff0c;那就是过去的自己。战胜自己…

Nginx 部署前端 Vue 项目全攻略

一、前期准备工作 要将 Vue 项目部署到 Nginx &#xff0c;需要做好以下准备工作&#xff1a; Nginx 的安装&#xff1a; 对于 Centos 系统&#xff0c;可以通过 yum install -y nginx 命令来安装 Nginx 。 对于 Windows 系统&#xff0c;需要在 Nginx 官网下载相应的安装包并…

数据迁移新技能,MongoDB轻松同步至ClickHouse

在当今数据驱动的世界中&#xff0c;企业的成功依赖于对数据的高效管理和精准分析。数据迁移是实现这些目标的关键环节&#xff0c;而选择合适的工具可以让这项工作变得更加轻松和高效。ETLCloud 是一款创新的 ETL&#xff08;提取、转换、加载&#xff09;工具&#xff0c;它提…

(三)了解MySQL 【用户创建和权限/索引】

一、创建用户 DCL主要用于定义数据库的安全性和访问权限&#xff0c;包括创建用户、授予权限、撤销权限等。 CREATE USER 属于DCL因为它关注的是数据库的安全性和用户管理 格式 create user 用户名来源地址 [identified by [password] 密码 ]; 用户名&#xff1a;指…

Web开发:ABP框架中的服务调用原则--避免服务之间的直接依赖

示意图 &#xff08;Service之间可以相互调用&#xff0c;使用依赖倒置原则&#xff1a;例如某个服务可注入到另一个服务&#xff0c;用法&#xff1a;private readonly IyyyService _yyyService;&#xff09; 命名约定 WebAPI 控制器: 建议命名为 XXXXController 或 XXXXMa…

Linux学习(15)-网络编程:滑动窗口、拥塞控制、udp

本节学习内容 1.滑动窗口&#xff08;1.滑动窗口的作用2.如果如果接收端填充的接收窗口为0&#xff0c;发送端接下来怎么处理3.糊涂窗口综合征4.tcp中nagle算法是什么&#xff09; 2.拥塞控制 3.udp协议特点及编程流程 本节可能会用到的指令 ifconfig查看自己的ip地址 pi…

Scrapy添加代理IP池:自动化爬虫的秘密武器

在网络爬虫的世界里&#xff0c;IP地址的频繁更换是防止被目标网站封禁的有效手段。通过在Scrapy中添加代理IP池&#xff0c;你可以轻松实现自动化的IP切换&#xff0c;提高数据抓取的效率和稳定性。今天&#xff0c;我们就来详细讲解一下如何在Scrapy中添加代理IP池&#xff0…

使用 nuxi analyze 命令分析 Nuxt 应用的生产包

title: 使用 nuxi analyze 命令分析 Nuxt 应用的生产包 date: 2024/8/29 updated: 2024/8/29 author: cmdragon excerpt: 使用 nuxi analyze 命令可以帮助你深入了解生产包的结构和大小,从而做出针对性的优化。通过定期分析生产包,你可以识别并解决性能瓶颈,提高应用的加…

Linux 数据结构 内核链表 栈

内核链表&#xff1a; 1.一种链表结构能够操作多种类型的数据对象 2.节点包含数据变成数据包含节点 /*Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>This file is part of GlusterFS.This file is licensed to you under your choice of the…

从雨滴到数据--双翻斗雨量传感器让雨量可视化

CG-04-D1双翻斗雨量传感器是一种重要的水文、气象仪器&#xff0c;主要用于测量自然界中的降雨量&#xff0c;并将降雨量转换为以开关量形式表示的数字信息量输出&#xff0c;以满足信息传输、处理、记录和显示等需求。以下是对双翻斗雨量传感器的详细介绍&#xff1a; 一、工…

手机号码归属地查询如何用PHP进行调用

一、什么是手机号归属地查询接口&#xff1f; 手机号码归属地查询接口又叫手机号归属地、手机号信息查询、手机号查询&#xff0c;通过手机号查询归属地信息、是否虚拟运营商等。该接口可支持三大运营商&#xff0c;移动、电信、联通等。 二、手机号归属地查询接口适用场景有…

《Toolformer: Language Models Can Teach Themselves to Use Tools》论文解读

0. 引言 《Toolformer: Language Models Can Teach Themselves to Use Tools》 论文主要探讨了语言模型&#xff08;LMs&#xff09;在解决新任务时的能力和局限性&#xff0c;并提出了一个名为 Toolformer 的新方法。该方法通过简单 API 接口将外部工具与 LMs 相结合&#xf…

趣味算法------过河卒

目录 ​编辑 题目描述 解题思路 具体代码 总结 问题描述&#xff1a; 解决方案&#xff1a; 代码实现&#xff1a; 关键点&#xff1a; 题目描述 棋盘上 A 点有一个过河卒&#xff0c;需要走到目标 B 点。卒行走的规则&#xff1a;可以向下、或者向右。同时在棋盘上 C…

海外仓系统是什么?你想要了解的都在这里

对从事海外仓经营的人来说&#xff0c;海外仓系统肯定是不陌生的。但是对一些刚进入海外仓领域的货代代理&#xff0c;或一些家庭海外仓的经营者来说&#xff0c;还是有些糊涂&#xff0c;不知道自己到底要不要引入这样的管理系统&#xff0c;引入海外仓系统之后到底能帮自己做…

尚品汇-订单接口实现(四十)

目录&#xff1a; &#xff08;1&#xff09;搭建service-order-client模块 &#xff08;2&#xff09;微服务之间用户信息传递 &#xff08;3&#xff09;在web-all模块中添加接口 &#xff08;4&#xff09;下订单 &#xff08;1&#xff09;搭建service-order-client模…

DDD设计方法-1-初识DDD

前情提要&#xff1a;一共包含 如下六篇文章&#xff08;篇幅精简&#xff0c;快速入门&#xff09; 1、初识DDD 2、聚合、实体、值对象 3、仓储&#xff0c;封装持久化数据 4、端口和适配器 5、领域事件 6、领域服务&#xff0c;实现约定 DDD设计理念-快速入门 DDD&#xff0…

雨水回用一体化设备

雨水回用一体化设备集提升、回用&#xff08;变频恒压供水&#xff09;、排泥系统&#xff1b;絮凝、消毒&#xff08;加药、紫外线&#xff09;、曝气系统&#xff1b;过滤&#xff08;初、精&#xff09;系统&#xff08;全自动自清洗过滤器/石英砂过滤器/多介质过滤器/精密碟…

没错,一分钟告诉你TCP和UDP之间的区别!

TCP (Transmission Control Protocol&#xff09;和UDP (User Datagram Protocol&#xff09;是两种常用的传输层协议&#xff0c;用于在计算机网络中传输数据。 TCP是一种面向连接的协议&#xff0c;提供可靠的数据传输。它通过建立连接、数据分段、流量控制、拥塞控制和错误校…

【论文阅读】skill code 和 one-shot manipulate

文章目录 1. Interpretable Robotic Manipulation from Language针对痛点和贡献摘要和结论引言模型框架实验思考不足之处 2. One-Shot Imitation Learning with Invariance Matching for Robotic Manipulation针对痛点和贡献摘要和结论引言模型框架实验 1. Interpretable Robot…

深入理解快排【C语言版】

目录 一、快排介绍及其思想 二、hoare版本 三、前后指针版 四、挖坑法 五、优化版本 5.1 三数取中 5.2 小区间优化 六 、非递归实现快排 七、三路划分 八、introsort 小结 一、快排介绍及其思想 快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一…