常用方法
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进行交换。
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;
}