使用ItemDecoration打造列表顶部悬浮效果

ItemDecoration基本用法

ItemDecoration是用来给RecyclerView添加分隔线的,我们可以通过自定义ItemDecoration来实现各种效果,我们先来看一下基本用法。
首先我们继承ItemDecoration,要实现三个方法

        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
        }

        @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDrawOver(c, parent, state);
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
        }
  • getItemOffsets():使用这个方法来给其中一个item(view)添加间隔,用来绘制分割线,比如我们通过parent.getChildAdapterPosition(view)得到这个item的position,然后我们使用outRect.top = 100来使这个item的上面多出100高度的矩形空间用来绘制,对应的left之类的可以类比。
  • onDraw():用来绘制分割线,一般在这个方法里遍历parent的全部child,然后得到position(注意这里的child只能得到展示在页面的,所以需要额外计算得到真实的position)根据情况看看需不需要绘制分割线。
  • onDrawOver():和onDraw()很像,区别在于这个绘制的显示在最上方,原因马上说。

在RecyclerView绘制的时候,首先调用onDraw绘制分割线,然后调用自身的onDraw绘制自己(各种item),最后调用onDrawOver绘制,三个绘制用的一个Canvas,所以最后绘制的会覆盖之前的。

代码实战

刚看理论肯定云里雾里,这时候就需要敲代码来加深理解和记忆了,我们要完成的效果如下
这里写图片描述

在RecyclerView中添加数据

data

    private String[] data = {"1-你好", "1-你好", "1-你好", "1-你好", "1-你好", "1-你好", "1-你好",
                             "2-OK", "2-OK", "2-OK", "2-OK", "2-OK", "2-OK", "2-OK",
                             "3-你好", "3-你好", "3-你好", "3-你好", "3-你好", "3-你好", "3-你好",
                             "4-OK", "4-OK", "4-OK", "4-OK", "4-OK", "4-OK", "4-OK",};

Adapter

    RecyclerView.Adapter<RecyclerView.ViewHolder> adapter = new RecyclerView.Adapter<RecyclerView.ViewHolder>() {
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v =  LayoutInflater.from(MainActivity.this).inflate(R.layout.item_layout, parent, false);
            return new MyViewHolder(v);
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            MyViewHolder viewHolder = (MyViewHolder) holder;
            viewHolder.textView.setText(data[position]);
        }

        @Override
        public int getItemCount() {
            return data.length;
        }

        class MyViewHolder extends RecyclerView.ViewHolder {

            public TextView textView;

            public MyViewHolder(View itemView) {
                super(itemView);
                textView = (TextView) itemView.findViewById(R.id.text);
            }
        }
    };

item_layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:textSize="20sp"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#000"/>

</LinearLayout>

给每一组前面添加组名

首先我们需要两个方法分别判断是不是该组第一个item和返回改组的组名

    private String getGroupName(int index) {
        return "第" + (index / 7 + 1) + "组";
    }

    private boolean isFirstOfGroup(int index) {
        return index % 7 == 0;
    }

然后我们先对每一组第一个item上部添加空间来绘制

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            int index = parent.getChildAdapterPosition(view);
            if (isFirstOfGroup(index)) {
                outRect.top = mTopHeight;
            }
        }

这里mTopHeight是组名的高度,然后我们开始绘制

        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
            // 得到item真实的left和right(减去parent的padding)
            int left = parent.getPaddingLeft();
            int right = parent.getWidth() - parent.getPaddingRight();

            for (int i = 0; i != parent.getChildCount(); i++) {
                // 直接获得的child只有当前显示的,所以就算i是0的index也只是当前第一个,而不是所有第一个
                View child = parent.getChildAt(i);
                int index = parent.getChildAdapterPosition(child);
                if (isFirstOfGroup(index)) {
                    // 每组第一个item都留有空间来绘制
                    int top = child.getTop() - mTopHeight;
                    int bottom = child.getTop();
                    // 绘制背景色
                    Paint paint = new Paint();
                    paint.setColor(Color.YELLOW);
                    c.drawRect(left, top, right, bottom, paint);
                    // 绘制组名
                    paint.setColor(Color.BLACK);
                    paint.setTextSize(60);
                    paint.setTextAlign(Paint.Align.LEFT);
                    paint.setAntiAlias(true);
                    c.drawText(getGroupName(index), left, bottom, paint);
                }
            }
        }

效果如下
这里写图片描述

添加顶部悬浮

我们先来分析一下,平常情况就是顶部绘制一个组名,我们可以通过onDrawOver来覆盖后面的内容达到悬浮的效果。当我们目前页面第二个item是下一组的第一个item的时候情况就不一样了(也就是下一个组马上就要到达顶部的时候),我们的顶部组就会向上发生偏移(像被顶上去的样子),这时候我们只需要计算绘制的bottom是多少就好了(top就是0)。我们可以这样来做

            int bottom = 0;
            if (isFirstOfGroup(index + 1)) {
                // 下一个组马上到达顶部
                bottom = Math.min(child.getBottom(), mTopHeight);
            } else {
                // 普通情况
                bottom = mTopHeight;
            }

当下一个组马上到达顶部的时候,我们选择mTopHeight和child.getBottom()中比较小的来作为bottom,这样就可以保证,当悬浮组名bottom超过该组最后一个item的bottom的时候改为最后一个item的bottom,最后消失。
最后看一下效果
这里写图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值