Android RecyclerView实现吸顶动态效果流程分析

 更新时间:2022年12月22日 09:53:07   作者:别偷我的猪_09  
RecyclerView是Android一个更强大的控件,其不仅可以实现和ListView同样的效果,还有优化了ListView中的各种不足。其可以实现数据纵向滚动,也可以实现横向滚动(ListView做不到横向滚动)。接下来讲解RecyclerView的用法

一、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://pan.baidu.com/s/11I8dtWts3BZjYMgNvzanTw?pwd=bvx9

到此这篇关于Android RecyclerView实现吸顶动态效果流程分析的文章就介绍到这了,更多相关Android RecyclerView内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 关于Android多渠道打包问题看这一篇就够了

    关于Android多渠道打包问题看这一篇就够了

    这篇文章主要介绍了关于Android程序的多渠道打包方法,还不会的同学快进来学习下吧,建议收藏以防迷路
    2021-08-08
  • Android中Intent习惯用法

    Android中Intent习惯用法

    这篇文章主要介绍了Android中Intent习惯用法,演示了如何在Android中利用Intent进行拍照、摄像、打电话、发短信、发邮件等,感兴趣的小伙伴们可以参考一下
    2015-12-12
  • Android代码实现图片和文字上下布局

    Android代码实现图片和文字上下布局

    在Android开发中经常会需要用到带文字和图片的button,下面来给大家介绍使用radiobutton实现图片和文字上下布局或左右布局。感兴趣的朋友一起学习吧
    2015-11-11
  • Android开发实现读取Assets下文件及文件写入存储卡的方法

    Android开发实现读取Assets下文件及文件写入存储卡的方法

    这篇文章主要介绍了Android开发实现读取Assets下文件及文件写入存储卡的方法,涉及Android文件与目录的读取、写入、转换等相关操作技巧,需要的朋友可以参考下
    2017-10-10
  • Android自定义view实现圆环效果实例代码

    Android自定义view实现圆环效果实例代码

    本文通过实例代码给大家介绍了Android自定义view实现圆环效果,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-07-07
  • Android开发中一个简单实用的调试应用技巧分享

    Android开发中一个简单实用的调试应用技巧分享

    这篇文章主要跟大家分享了一个简单实用的Android调试应用技巧,文中介绍的非常详细,相信对大家具有一定的参考学习价值,需要的朋友下面来一起看看吧。
    2017-05-05
  • Flutter应用框架搭建之屏幕适配详解

    Flutter应用框架搭建之屏幕适配详解

    因移动设备的多样性,特别是 Android 的碎片化严重,存在各种各样的分辨率,而 Flutter 跨平台开发又需同时支持 Android 和 iOS ,为尽可能的还原设计图效果提升用户体验,屏幕适配就势在必行了。本文将详细讲解Flutter屏幕适配的方法,需要的可以参考一下
    2022-03-03
  • Android简单实现圆盘抽奖界面

    Android简单实现圆盘抽奖界面

    这篇文章主要介绍了Android简单实现圆盘抽奖界面的相关资料,需要的朋友可以参考下
    2016-01-01
  • Android ShimmerLayout实现微光效果解析

    Android ShimmerLayout实现微光效果解析

    这篇文章主要为大家详细介绍了Android ShimmerLayout实现微光效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Android Activity生命周期详解

    Android Activity生命周期详解

    本篇文章主要介绍Android Activity的生命周期,这里整理了关于Activity的生命周期资料并附测试代码示例,有兴趣的小伙伴可以参考下
    2016-08-08

最新评论