Android RecyclerView实现吸顶动态效果,详细分析

简介: Android RecyclerView实现吸顶动态效果,详细分析

链接:https://download.csdn.net/download/JasonXu94/87786702


一、ItemDecoration


ItemDecoration 允许应用给具体的

View 添加具体的图画或者 layout 的偏移,对于绘制 View之间的分割线,视觉分组边界等等是非常有用的。

当我们调用 addItemDecoration()

方法添加 decoration 的时候,RecyclerView 就会调用该类的 onDraw 方法去绘制分割线,也就是说:分割线是绘制出来的。

RecyclerView.addItemDecoration()

RecyclerView.ItemDecoration,该类为抽象类,官方目前只提供了一个实现类

DividerItemDecoration。

public abstract static class ItemDecoration


public class DividerItemDecoration extends RecyclerView.ItemDecoration


里面有3个方法:

onDraw():

在提供给RecyclerView的画布上绘制任何适当的装饰。通过此方法绘制的任何内容都将在绘制项目视图之前被绘制,因此将出现在视图的下方。

public void onDraw(Canvas c, RecyclerView parent, State state) {
            onDraw(c, parent);
        }

绘制效果


onDrawOver():在提供给RecyclerView的画布上绘制任何适当的装饰。通过这种方法绘制的任何内容都将在项目视图被绘制之后被绘制,因此将出现在视图上方。



public void onDrawOver(Canvas c, RecyclerView parent, State state) {
            onDrawOver(c, parent);
        }


getItemOffsets():检索给定项的任何偏移量。outRect的每个字段指定项目视图应该插入的像素数,类似于padding或margin。默认实现将outRect的界限设置为0,并返回。

public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
            getItemOffsets(outRect, ((LayoutParams)view.getLayoutParams()).getViewLayoutPosition(),
                    parent);
        }


二、实现RecyclerView吸顶效果


1、实现一个简单的RecyclerView

下面这个RecyclerView的实现细节略。


2、通过ItemDecoration画分割线

自定义ItemDecoration。

创建 fruitDecotion 类 继承 ItemDecoration,并实现onDraw、onDrawover、getItemOffsets三个方法。


RecyclerView调用自定义的fruitDecoration

RecyclerView.addItemDecoration(new fruitDecoration(this))


在getItemOffsets画分割线,当是组名时,预留更大的空间(即分割线的height比普通分割线大)。

@Override
    public 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 fruitAdapter){
            // parent.getAdapter()获取到当前RecyclerView的Adapter
            fruitAdapter adapter = (fruitAdapter) parent.getAdapter();
            // 获取当前view的位置
            int position = parent.getChildLayoutPosition(view);
            if (adapter.isGroupHeader(position)) {
                // 如果是头部,则预留更大的地方
                outRect.set(0, groupHeaderHeight, 0, 0);
            }else {
                outRect.set(0, 4, 0, 0);
            }
        }

实现效果


3、画出每个分组的组名

当是组名时,在较大分割线哪里画出组名。在onDraw()方法里实现

@Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (parent.getAdapter() instanceof fruitAdapter) {
            fruitAdapter adapter = (fruitAdapter) parent.getAdapter();
            //当前屏幕中item的个数
            int count = parent.getChildCount();
            // 获取当前RecyclerView距离屏幕左边的padding
            int left = parent.getPaddingLeft();
            // right == RecyclerView的宽度 - RecyclerView的右padding
            int right = parent.getWidth() - parent.getPaddingRight();
            for (int i = 0; i < count; i++) {
                //获取对应 i 的 view
                View view = parent.getChildAt(i);
                // 获取 i 的 view 的布局位置
                int position = parent.getChildLayoutPosition(view);
                // 判断是否是头部
                boolean isGroupHeader = adapter.isGroupHeader(position);
                // i 的 view 是头部并且 当前view的top位置距离屏幕的顶部还有距离
                if(isGroupHeader && view.getTop() - groupHeaderHeight - parent.getPaddingTop() >=0) {
                    c.drawRect(left, view.getTop() - groupHeaderHeight, right, view.getTop(), headPaint);
                    String groupName = adapter.getGroupName(position);
                    textPaint.getTextBounds(groupName, 0, groupName.length(), textRect);
                    c.drawText(groupName, left + 20, view.getTop() - groupHeaderHeight / 2f + textRect.height() / 2f, textPaint);
                }else if(view.getTop() - groupHeaderHeight - parent.getPaddingTop() >=0){
                    //分割线
                    c.drawRect(left, view.getTop() - 4, right, view.getTop(), headPaint);
                }
            }
       }
    }


实现效果


4、实现吸顶效果

因为onDrawOver方法是在itemView画了之后才画,所以组名的吸顶是写在onDrawOver方法里。需要注意的时,当我们的第二个组名到顶部的时候,要把当前顶部的组名给替换掉。

 @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        if (parent.getAdapter() instanceof fruitAdapter) {
            fruitAdapter adapter = (fruitAdapter) parent.getAdapter();
            //TODO 返回可见区域内的第一个item的position
            int position =((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
            // 获取第一个item的view
            View itemView = parent.findViewHolderForAdapterPosition(position).itemView;
            int left = parent.getPaddingLeft();
            int right = parent.getWidth() - parent.getPaddingRight();
            int top = parent.getPaddingTop();
            //TODO 当第二个是头部时,
            boolean isGroupHeader = adapter.isGroupHeader(position + 1);
            if (isGroupHeader) {
                int bottom = Math.min(groupHeaderHeight, itemView.getBottom() - parent.getPaddingTop());
                c.drawRect(left, top, right, top + bottom, headPaint);
                String groupName = adapter.getGroupName(position);
                textPaint.getTextBounds(groupName, 0, groupName.length(), textRect);
                c.drawText(groupName, left + 20, top + bottom - groupHeaderHeight / 2f
                        + textRect.height() / 2f, textPaint);
            }else { // 如果不是头部, 即普通的itemView,则当前的头部一直固定在顶部
                c.drawRect(left, top, right, top + groupHeaderHeight, headPaint);;
                String groupName = adapter.getGroupName(position);
                textPaint.getTextBounds(groupName, 0, groupName.length(), textRect);
                c.drawText(groupName, left + 20, top + groupHeaderHeight / 2f
                        + textRect.height() / 2f, textPaint);
            }
        }
    }


实现效果


完整demo


链接:https://download.csdn.net/download/JasonXu94/87786702

到此这篇关于Android RecyclerView实现吸顶动态效果流程分析的文章就介绍到这了



相关文章
|
11月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
1012 4
|
5月前
|
Android开发
Android自定义view之利用PathEffect实现动态效果
本文介绍如何在Android自定义View中利用`PathEffect`实现动态效果。通过改变偏移量,结合`PathEffect`的子类(如`CornerPathEffect`、`DashPathEffect`、`PathDashPathEffect`等)实现路径绘制的动态变化。文章详细解析了各子类的功能与参数,并通过案例代码展示了如何使用`ComposePathEffect`组合效果,以及通过修改偏移量实现动画。最终效果为一个菱形图案沿路径运动,源码附于文末供参考。
|
5月前
|
Android开发 开发者
Android自定义view之利用drawArc方法实现动态效果
本文介绍了如何通过Android自定义View实现动态效果,重点使用`drawArc`方法完成圆弧动画。首先通过`onSizeChanged`进行测量,初始化画笔属性,设置圆弧相关参数。核心思路是不断改变圆弧扫过角度`sweepAngle`,并调用`invalidate()`刷新View以实现动态旋转效果。最后附上完整代码与效果图,帮助开发者快速理解并实践这一动画实现方式。
139 0
|
11月前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
135 2
|
12月前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
391 15
Android 系统缓存扫描与清理方法分析
|
11月前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
468 21
|
10月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
190 8
|
12月前
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
Android开发 开发者 索引
Android实战经验之如何使用DiffUtil提升RecyclerView的刷新性能
本文介绍如何使用 `DiffUtil` 实现 `RecyclerView` 数据集的高效更新,避免不必要的全局刷新,尤其适用于处理大量数据场景。通过定义 `DiffUtil.Callback`、计算差异并应用到适配器,可以显著提升性能。同时,文章还列举了常见错误及原因,帮助开发者避免陷阱。
924 9

热门文章

最新文章