Android RecyclerView字母索引,一行代码实现吸顶效果,简单实用,附demo地址和截图

分享一个实用的列表吸顶效果
demo源码地址

字母索引,列表吸顶

效果图

效果图

核心代码

```java
package com.laj.wxaddperson.hover_item;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.TypedValue;
import android.view.View;

import androidx.recyclerview.widget.RecyclerView;


public class HoverItemDecoration extends RecyclerView.ItemDecoration {

    private Context context;

    private int width;

    /**
     * 分组item的高度
     */
    private int itemHeight;
    /**
     * 分割线的高度
     */
    private int itemDivideHeight;
    /**
     * 分组text距离左边的距离
     */
    private int itemTextPaddingLeft;

    /**
     * 分组item的画笔
     */
    private Paint itemPaint;
    /**
     * 分组item的颜色
     */
    private int itemHoverPaintColor=0xFFf4f4f4;
    /**
     * 分组文字的颜色
     */
    private int textPaintColor=0xFF999999;

    /**
     * 悬停item的画笔
     */
    private Paint itemHoverPaint;
    /**
     * 文字的画笔
     */
    private Paint textPaint;
    /**
     * 绘制文字的矩形边框
     */
    private Rect textRect = new Rect();
    /**
     * 分组字母的回调(一般是取的分组的大写字母)
     */
    private BindItemTextCallback bindItemTextCallback;


    public HoverItemDecoration(Context content, BindItemTextCallback bindItemTextCallback) {
        this.context = content;
        this.bindItemTextCallback = bindItemTextCallback;

        width = content.getResources().getDisplayMetrics().widthPixels;

        itemPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        itemPaint.setColor(itemHoverPaintColor);

        itemHoverPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        itemHoverPaint.setColor(itemHoverPaintColor);

        itemHeight = dp2px(30);
        itemTextPaddingLeft = dp2px(20);
        itemDivideHeight = dp2px(1);

        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setColor(textPaintColor);
        textPaint.setTextSize(sp2px(15));


    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        int count = parent.getChildCount();
        for (int i = 0; i < count; i++) {
            View view = parent.getChildAt(i);
            //分组item的顶部和底部
            int itemTop = view.getTop() - itemHeight;
            int itemBottom = view.getTop();

            //可见item在adapter中真实的位置
            int position = parent.getChildAdapterPosition(view);

            //获取回调的分组文字(一般是字母)
            String text = bindItemTextCallback.getItemText(position);

            //如果是一组中第一个的话绘制出分组的item和文字,否则绘制分割线
            if (isFirstInGroup(position)) {
                c.drawRect(0, itemTop, width, itemBottom, itemPaint);
                drawText(c, itemTop, itemBottom, text);
            } else {
                c.drawRect(0, view.getTop() - itemDivideHeight, width, view.getTop(), itemHoverPaint);
            }

        }
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        //绘制悬停的view

        int count = parent.getChildCount();

        if (count > 0) {
            //悬停只是第一个位置悬停,所以只取第一个view进行设置
            View firstView = parent.getChildAt(0);

            int position = parent.getChildAdapterPosition(firstView);
            String text = bindItemTextCallback.getItemText(position);

            //如果悬停view的底部小于悬停布局的高度说明正在上滑,就让他随着滑动逐渐滑进去,否则就固定悬停位置不边
            //isFirstInGroup(position+1)是下一个item是另外分组第一个的时候当前item才滚动上去
            if (firstView.getBottom() <= itemHeight && isFirstInGroup(position + 1)) {
                c.drawRect(0, 0, width, firstView.getBottom(), itemHoverPaint);
                drawText(c, firstView.getBottom() - itemHeight, firstView.getBottom(), text);
            } else {
                c.drawRect(0, 0, width, itemHeight, itemHoverPaint);
                drawText(c, 0, itemHeight, text);
            }
        }

    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int position = parent.getChildAdapterPosition(view);
        //如果是分组第一个就留出绘制item的高度
        if (isFirstInGroup(position)) {
            outRect.top = itemHeight;
        }else {
            outRect.top = itemDivideHeight;
        }

    }


    /**
     * 绘制文字
     *
     * @param canvas 画布
     */
    private void drawText(Canvas canvas, int itemTop, int itemBottom, String textString) {

        textRect.left = itemTextPaddingLeft;
        textRect.top = itemTop;
        textRect.right = textString.length();
        textRect.bottom = itemBottom;

        Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
        int baseline = (textRect.bottom + textRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
        //文字绘制到整个布局的中心位置
        canvas.drawText(textString, textRect.left, baseline, textPaint);
    }


    private boolean isFirstInGroup(int position) {
        if (position == 0) {
            return true;
        } else {
            String prevItemText = bindItemTextCallback.getItemText(position - 1);
            String currentItemText = bindItemTextCallback.getItemText(position);
            //上一个和当前位置的值一样说明是同一个组的否则就是新的一组
            if (prevItemText.equals(currentItemText)) {
                return false;
            } else {
                return true;
            }

        }
    }

    public interface BindItemTextCallback {
        String getItemText(int position);
    }

    /**
     * dp 2 px
     *
     * @param dpVal
     */
    protected int dp2px(int dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpVal, context.getResources().getDisplayMetrics());
    }

    /**
     * sp 2 px
     *
     * @param spVal
     * @return
     */
    protected int sp2px(int spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, context.getResources().getDisplayMetrics());

    }

}



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现RecyclerView悬浮效果,可以使用以下步骤: 1. 创建一个布局文件,包含两个部分:一个用于悬浮显示的视,一个用于RecyclerView。 2. 在Activity或Fragment中,找到RecyclerView并设置布局管理器和适配器。 3. 创建一个自定义的RecyclerView.ItemDecoration类,用于绘制悬浮视。 4. 在自定义的ItemDecoration类中,重写getItemOffsets()方法,在该方法中计算悬浮视的高度,并将其应用到RecyclerView的第一个可见项之上。 5. 在自定义的ItemDecoration类中,重写onDraw()方法,在该方法中绘制悬浮视。 6. 在Activity或Fragment中,为RecyclerView添加ItemDecoration。 下面是一个简单的示例代码: 1. 创建布局文件(例如:activity_main.xml): ```xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/floating_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Floating View" android:background="#FF0000" android:textColor="#FFFFFF" android:padding="16dp" android:visibility="gone" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/floating_view" /> </RelativeLayout> ``` 2. 在Activity或Fragment中,设置RecyclerView的布局管理器和适配器: ```java // 找到RecyclerView RecyclerView recyclerView = findViewById(R.id.recyclerview); // 设置布局管理器 recyclerView.setLayoutManager(new LinearLayoutManager(this)); // 设置适配器 recyclerView.setAdapter(adapter); ``` 3. 创建一个自定义的ItemDecoration类(例如:FloatingHeaderDecoration.java): ```java public class FloatingHeaderDecoration extends RecyclerView.ItemDecoration { private View mFloatingView; public FloatingHeaderDecoration(View floatingView) { mFloatingView = floatingView; } @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.getChildAdapterPosition(view) == 0) { outRect.top = mFloatingView.getHeight(); } } @Override public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDraw(canvas, parent, state); int top = parent.getPaddingTop(); int bottom = top + mFloatingView.getHeight(); int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); mFloatingView.setVisibility(View.VISIBLE); mFloatingView.layout(left, top, right, bottom); mFloatingView.draw(canvas); } } ``` 4. 在Activity或Fragment中,为RecyclerView添加ItemDecoration: ```java // 找到悬浮视 View floatingView = findViewById(R.id.floating_view); // 创建自定义的ItemDecoration并添加到RecyclerView recyclerView.addItemDecoration(new FloatingHeaderDecoration(floatingView)); ``` 这样就实现RecyclerView的悬浮效果。悬浮视会在滚动时始终保持在部,并且不会被其他项覆盖。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值