RecyclerView及与其相关的类

常用方法

        getChildAdapterPosition(View):获取view在Adapter中的position。
        getChildLayoutPosition(View):获取view在layout中的position。大部分情况下,它与getChildAdapterPosition()是相同的。但是当新布局尚未完成时(比如新增动画尚未执行完毕时),两者的值是不同的。如下:
if(view != null){
    Log.e(TAG,"still adapter.position = "+recyclerView.getChildAdapterPosition(view)+",layout = "+recyclerView.getChildLayoutPosition(view));
}
view = recyclerView.getChildAt(1);
Log.e(TAG,"before adapter.position = "+recyclerView.getChildAdapterPosition(view)+",layout = "+recyclerView.getChildLayoutPosition(view));
recyclerView.getAdapter().notifyItemInserted(1);
Log.e(TAG,"after adapter.position = "+recyclerView.getChildAdapterPosition(view)+",layout = "+recyclerView.getChildLayoutPosition(view));
        连续调用该方法两次,输出的结果如下:
            //第一次
            before adapter.position = 1,layout = 1
            after adapter.position = 2,layout = 1
            //第二次
            still adapter.position = 2,layout = 2
            before adapter.position = 1,layout = 1
            after adapter.position = 2,layout = 1
        第一次中,after的adapter的pos为2。这是因为我们调用了新insert了一个item,原来的1就被挤到了2,所以adapter.pos就是2。但layout一直没有变化,这是因为insert时有一个动画,在进行第二次输出时,动画没有执行完毕,
所以在layout中该view的pos依旧是1。当第二次调用时,still中可以发现该view的layout.pos已经变成2了,因为新的布局已经生成,在该布局中view的pos就是2。
        getChildCount():获取当前可见的item的数量。注:是可见的item的数量,并不是所有的。

RecyclerView.Adapter

        跟recyclerView关联的Adapter,基本使用略,记得建一个public的ViewHolder即可。
        notifyItemRangeInserted():在指定的开始位置处插入了多个item。
        notifyItemRangeRemoved():从指定的开始位置处移除了多个item。
        notifyItemRangeChanged():从指定的开始位置处有多个item的内容发生了变化,需要进行更新。它有两个方法,多出来的Object对象是传递给观察者的(这些notify内部都是使用了观察者模式)。
        notifyItemRemoved(int):移除指定位置的item。
        notifyItemChanged(int):指定位置的item内容发生了变化。
        notifyItemChanged(int,Object):同上,Object也是传递到相应的观察者中。
        notifyItemInserted(int):在指定的位置处插入一个item。
        notifyItemMoved(int,int):指定位置的两个item进行交换。
        notifyDataSetChanged():与ListView的Adapter类似。它与上面几个方法的区别在于,该方法在增,删,改时不会引起动画的执行,但上面的会。

ItemDecoration

        允许对RecyclerView添加特殊的图案或者使item发生偏移。有以下三个方法:
        onDraw():为RecyclerView添加一些额外的修饰,该方法会在item绘制之前进行调用。也就是说它绘制的内容可能会被item给覆盖住——如果绘制在item的空格处就不会被挡住。
        onDrawOver():基本上与onDraw()类似,只不过在item绘制之后绘制。因此可能会覆盖住item的内容。
        getItemOffsets():获取每一个item的在各个方向的需要额外留出的空余量,各个方向空余量的值需要设置到第一个参数Rect对象中。比如一共有4列,对于720px的手机来说,每一列的宽度为180。如果每一个item的右侧空余量为5px,那么每一列的宽度为175px。

添加分隔线

        主要思路是:每一个item偏移一定的位置,在空出来的位置上绘制图形,这些图形就是分隔线。如下:

public class DividerItemDecor extends RecyclerView.ItemDecoration {
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        Paint p = new Paint();
        p.setColor(Color.RED);
        p.setStyle(Paint.Style.FILL);
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + 5;
            c.drawRect(0,top,c.getWidth(),bottom,p);//绘制一个矩形,充当分隔线
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        outRect.set(0,0,0,5);//每一个item的bottom内嵌5px
    }
}
        上述代码中会出现一个问题:因为getChildCount()返回的是可见item的个数,所以随着不断的滑动同一个item对应的i值会不断变化。因此,如果分隔线的颜色跟i的取值有关的话,就会分隔线不断的变化。如在drawRect()之前添加上如下代码(主要目的是为了让分隔线有两种颜色):
            if(i % 2!=0){
                mPaint.setColor(Color.RED);
            }else{
                mPaint.setColor(Color.YELLOW);
            }
        那么,当屏幕滚动时,同个item下的分隔线的颜色会不断切换。为解决该问题,应将i转换成view的adapter.pos。如下:
            int position = parent.getChildAdapterPosition(child);
            if(position % 2!=0){
                mPaint.setColor(Color.RED);
            }else{
                mPaint.setColor(Color.YELLOW);
            }
        这也是处理不同位置的Item的常用思路:拿到adapter position,再根据这个position进行操作

SortedList

        一个与RecyclerView配套的使用的List集合。它是一个自动对内部元素进行排序的List集合。其常用方法与一般的List集合类似,无非是增删改等,别的方法如下:

        recalculatePositionOfItemAt():重新计算某个位置上item的应该所处的位置。比如某个item被勾选了,它应该出现在最前面等,这个时候就需要重新计算位置。其示例如下:

        @Override
        public TodoViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
            return new TodoViewHolder(
                    mLayoutInflater.inflate(R.layout.sorted_list_item_view, parent, false)) {
                @Override
                void onDoneChanged(boolean isDone) {//这个回调根据具体业务不同而不同,比如点击时的回调等,需要换成自己的回调
                    int adapterPosition = getAdapterPosition();//使用adapter position
                    if (adapterPosition == RecyclerView.NO_POSITION) {
                        return;
                    }
                    mBoundItem.mIsDone = isDone;
                    mData.recalculatePositionOfItemAt(adapterPosition);//通知重新计算位置
                }
            };
        }

        beginBatchedUpdates()endBatchedUpdates():类似于事务,在批量添加或删除之前之后调用。如下:

         mSortedList.beginBatchedUpdates();
          try {
              mSortedList.add(item1)
              mSortedList.add(item2)
              mSortedList.remove(item3)
             ...
         } finally {
             mSortedList.endBatchedUpdates();
          }

使用方法如下(v7demo):

            mData = new SortedList<Item>(Item.class, new SortedListAdapterCallback<Item>(this) {
                @Override
                public int compare(Item t0, Item t1) {//对两个item进行比较,用于判断前后位置
                    if (t0.mIsDone != t1.mIsDone) {
                        return t0.mIsDone ? 1 : -1;
                    }
                    int txtComp = t0.mText.compareTo(t1.mText);
                    if (txtComp != 0) {
                        return txtComp;
                    }
                    if (t0.id < t1.id) {
                        return -1;
                    } else if (t0.id > t1.id) {
                        return 1;
                    }
                    return 0;
                }

                @Override
                public boolean areContentsTheSame(Item oldItem,
                                                  Item newItem) {//两个item的内容是否一样
                    return oldItem.mText.equals(newItem.mText);
                }

                @Override
                public boolean areItemsTheSame(Item item1, Item item2) {//两个item是否是同一个。
                    return item1.id == item2.id;
                }
            });
下面以add()为例,分析上面的后两个函数的作用:

    private int add(T item, boolean notify) {
        int index = findIndexOf(item, mData, 0, mSize, INSERTION);//二分查找当前item的位置
        if (index == INVALID_POSITION) {
            index = 0;
        } else if (index < mSize) {//如果有
            T existing = mData[index];
            if (mCallback.areItemsTheSame(existing, item)) {//判断两个item是否一样
                if (mCallback.areContentsTheSame(existing, item)) {//判断内容是否一样
                    //no change but still replace the item
                    mData[index] = item;
                    return index;
                } else {
                    mData[index] = item;
                    mCallback.onChanged(index, 1);//通知某个位置上的内容修改了,需要刷新
                    return index;
                }
            }
        }
        addToData(index, item);//如果没有,就直接将item添加到集合中
        if (notify) {
            mCallback.onInserted(index, 1);//通过新添加了一个item
        }
        return index;
    }




  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值