当前位置: 首页 > news >正文

【安卓笔记】RecyclerView之ItemDecoration实现吸顶效果

0. 环境:

电脑:Windows10

Android Studio: 2024.3.2

编程语言: Java

Gradle version:8.11.1

Compile Sdk Version:35

Java 版本:Java11

1. ItemDecoration简单介绍

itemDecoration允许给具体的view添加具体的图画或者layout的偏移。大部分用于给每个item之间画分割线。

调用方法:recyclerView.addItemDecoration()

我们将使用ItemDecoration,来实现吸顶效果

2. 吸顶效果展示

应用场景:城市--省份,姓氏--首字母,列表--首字母 等

3. 实现步骤:

 关键代码:

ProvinceDecoration.java

package com.liosen.androidnote;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;public class ProvinceDecoration extends RecyclerView.ItemDecoration {private Context context;private int provinceHeight; // 省份部分高度private Paint headPaint;    // 头部画笔,即 吸顶那部分用到的画笔private Paint textPaint;    // 文字画笔private Rect textRect;      // 文字Rectpublic ProvinceDecoration(Context context) {this.context = context;this.provinceHeight = dp2px(context, 100);  // 设置100,可调整headPaint = new Paint();                            // 实例化头部画笔headPaint.setColor(Color.GREEN);                    // 设置头部画笔的颜色textPaint = new Paint();                            // 实例化文字画笔textPaint.setTextSize(50);                          // 设置文字画笔的大小textPaint.setColor(Color.WHITE);                    // 设置文字画笔的颜色textRect = new Rect();                              // 设置文字画笔必须要Rect}@Overridepublic void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {super.onDraw(c, parent, state);if (parent.getAdapter() instanceof ProvinceAdapter) {ProvinceAdapter adapter = (ProvinceAdapter) parent.getAdapter();int count = parent.getChildCount(); // 获取当前屏幕item的个数int left = parent.getPaddingLeft(); // 获取paddingLeftint right = parent.getWidth() - parent.getPaddingRight();   // 计算rightfor (int i = 0; i < count; i++) {// 获取ViewView view = parent.getChildAt(i);// 获取view的布局位置int pos = parent.getChildLayoutPosition(view);// 是否为省份boolean isProvince = adapter.isProvince(pos);if (isProvince) {// 如果为省份,则用画笔画出头部部分:headPaint、文字部分textPainc.drawRect(left, view.getTop() - provinceHeight, right, view.getTop(), headPaint);String provinceName = adapter.getProvinceName(pos);textPaint.getTextBounds(provinceName, 0, provinceName.length(), textRect);c.drawText(provinceName, left + 10, view.getTop() - provinceHeight / 2 + textRect.height() / 2, textPaint);} else {// 如果是城市,则画出分割线c.drawRect(left, view.getTop() - 1, right, view.getTop(), headPaint);}}}}@Overridepublic void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {super.onDrawOver(c, parent, state);if (parent.getAdapter() instanceof ProvinceAdapter) {ProvinceAdapter adapter = (ProvinceAdapter) parent.getAdapter();// 返回可见区域内,第一个item的position// 注意!!!如果你的recyclerView被ScrollView或者NestedScrollView 包裹,此处只会返回0;// 解决方法: 1. 要么移除ScrollView/NestedScrollView//          2. 布局文件中设置recyclerView: android:nestedScrollingEnabled="false",并且android:layout_height="wrap_content"//          3. 自己手动计算可见位置int firstPos = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();// 获取对应position的itemViewView itemView = parent.findViewHolderForAdapterPosition(firstPos).itemView;int left = parent.getPaddingLeft();int top = parent.getPaddingTop();int right = parent.getWidth() - parent.getPaddingRight();// 当第二个是省份的时候boolean isProvince = adapter.isProvince(firstPos + 1);String provinceName = adapter.getProvinceName(firstPos);if (isProvince) {// 如果是省份,则需要向上推走 上一个省份int bottom = Math.min(provinceHeight, itemView.getBottom());c.drawRect(left, top, right, top + bottom, headPaint);textPaint.getTextBounds(provinceName, 0, provinceName.length(), textRect);c.drawText(provinceName, left + 10, top + bottom - provinceHeight / 2 + textRect.height() / 2, textPaint);} else {// 如果不是省份,则继续吸顶c.drawRect(left, top, right, top + provinceHeight, headPaint);textPaint.getTextBounds(provinceName, 0, provinceName.length(), textRect);c.drawText(provinceName, left + 10, top + provinceHeight / 2 + textRect.height() / 2, textPaint);}}}@Overridepublic void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {super.getItemOffsets(outRect, view, parent, state);if (parent.getAdapter() instanceof ProvinceAdapter) {ProvinceAdapter adapter = (ProvinceAdapter) parent.getAdapter();int pos = parent.getChildLayoutPosition(view);boolean isProvince = adapter.isProvince(pos);if (isProvince) {// 如果是省份,则 顶部腾出省份高度,也就是初始化的时候传入的100outRect.set(0, provinceHeight, 0, 0);} else {// 如果不是省份,则画出分割线,这边设置分割线高度为1outRect.set(0, 1, 0, 0);}}}/*** @return dp转px*/private int dp2px(Context context, float dpValue) {float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale * 0.5f);}
}

ProvinceAdapter.java(适配器adapter部分,没啥好讲的。基本操作)

package com.liosen.androidnote;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;import java.util.List;public class ProvinceAdapter extends RecyclerView.Adapter<ProvinceAdapter.CityVH> {private List<CityBean> cityList;    // recyclerView数据private Context context;public ProvinceAdapter(Context context, List<CityBean> cityList) {this.context = context;this.cityList = cityList;}/*** @return 是否为省份*/public boolean isProvince(int pos) {if (pos == 0) {return true;} else {String provinceName = getProvinceName(pos);String preProvinceName = getProvinceName(pos - 1);// 当前item和上一个item对比,如果相同,则不是省份;如果不相同,则为新的省份if (preProvinceName.equals(provinceName)) {return false;} else {return true;}}}public String getProvinceName(int pos) {return cityList.get(pos).getProvinceName();}@NonNull@Overridepublic CityVH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(context).inflate(R.layout.rv_item_city, null);return new CityVH(view);}@Overridepublic void onBindViewHolder(@NonNull CityVH holder, int position) {holder.textView.setText(cityList.get(position).getCityName());}@Overridepublic int getItemCount() {return cityList == null ? 0 : cityList.size();}public class CityVH extends RecyclerView.ViewHolder {TextView textView;public CityVH(@NonNull View itemView) {super(itemView);textView = itemView.findViewById(R.id.tv);}}
}

只要按照我贴的ProvinceDecoration部分代码,就可以实现吸顶功能。至于其他部分,根据自己项目的业务,稍微修改即可。

4. 优化部分

adapter部分,我推荐BaseRecyclerViewAdapterHelper,github:https://github.com/CymChad/BaseRecyclerViewAdapterHelper

5. 源码上传

源码已上传:https://download.csdn.net/download/liosen/91415153

给伸手党

6. 写在最后

至此,我们就简单完成了吸顶效果。UI部分按照项目修改即可

http://www.dtcms.com/a/289151.html

相关文章:

  • codepen使用
  • FFmpeg 图片处理
  • 数据结构 | 栈:构建高效数据处理的基石
  • 【高等数学】第四章 不定积分——第三节 分部积分法
  • 【深度学习新浪潮】什么是robotaxi?
  • 【设计模式C#】享元模式(用于解决多次创建对象而导致的性能问题)
  • MPLS转发
  • windows C#-本地函数
  • Docker Compose 配置
  • docker compose 编排容器 mysql Springboot应用
  • 使用pytorch创建模型时,nn.BatchNorm1d(128)的作用是什么?
  • gradle关于dependency-management的使用
  • SpringBoot 整合 Langchain4j 实现会话记忆存储深度解析
  • OpenCV 入门知识:图片展示、摄像头捕获、控制鼠标及其 Trackbar(滑动条)生成!
  • 【LeetCode刷题指南】--反转链表,链表的中间结点,合并两个有序链表
  • Day25| 491.递增子序列、46.全排列
  • SQL Server(2022)安装教程及使用_sqlserver下载安装图文
  • redis-plus-plus安装与使用
  • [BUG]关于UE5.6编译时出现“Microsoft.MakeFile.Targets(44,5): Error MSB3073”问题的解决
  • 30天打牢数模基础-SVM讲解
  • Facebook 开源多季节性时间序列数据预测工具:Prophet 快速入门 Quick Start
  • UE5多人MOBA+GAS 26、为角色添加每秒回血回蓝(番外:添加到UI上)
  • Go并发聊天室:从零构建实战
  • Mysql(事务)
  • 30个常用的Linux命令汇总和实战场景示例
  • 30天打牢数模基础-粒子群算法讲解
  • 详解Mysql索引合并
  • Jetpack - ViewModel、LiveData、DataBinding(数据绑定、双向数据绑定)
  • langchain调用本地ollama语言模型和嵌入模型
  • 梯度提升之原理