recycleview讲解1
recycleview讲解2
贴源码
++viewholder++
package com.example.hy.test.recyclerview;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.widget.TextView;
public class BaseViewholder extends RecyclerView.ViewHolder {
private SparseArray mViewSparseArray;
private View itemView;
public BaseViewholder(View itemView) {
super(itemView);
this.itemView = itemView;
mViewSparseArray = new SparseArray<>();
}
private T getView(int id) {
View view = mViewSparseArray.get(id);
if (view == null) {
view = itemView.findViewById(id);
mViewSparseArray.put(id, view);
}
return (T) view;
}
public void setText(int id, String data) {
TextView tv = getView(id);
tv.setText(data);
}
}
++adapter++
package com.example.hy.test.recyclerview;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
public abstract class QuickRecyclerAdapter extends RecyclerView.Adapter {
private List mList;
public QuickRecyclerAdapter(List list) {
mList = list;
}
@Override
public int getItemCount() {
return mList.size();
}
@Override
public BaseViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
return new BaseViewholder(setLayoutView(parent, viewType));
}
@Override
public void onBindViewHolder(BaseViewholder holder, int position) {
setData(holder, mList.get(position), position);
}
public abstract View setLayoutView(ViewGroup parent, int viewType);
public abstract void setData(BaseViewholder holder, T t, int position);
}
++使用实例++
List testStrings = new ArrayList<>();
QuickRecyclerAdapter quickRecyclerAdapter = new QuickRecyclerAdapter(testStrings) {
@Override
public View setLayoutView(ViewGroup parent, int viewType) {
return LayoutInflater.from(RecyclerViewDemo.this).inflate(R.layout.test_tv, parent, false);
}
@Override
public void setData(BaseViewholder holder, String str, int position) {
holder.setText(R.id.tv_test, str);
}
};
用来设置item之间的分割线
一、说明。
由google官方提供,在support.v7包下。但是得注意版本,测试在recyclerview22.0.0包下没有该类。design25.0.0包下存在。(之后design包整合了recyclerview,recyclerview单独的包也有发布,可以视情况使用)。
二、使用
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
三、源码解析
构造函数中获得系统属性:android:listDivider
private static final int[] ATTRS = new int[]{ android.R.attr.listDivider };
private Drawable mDivider;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);//获得需要画在画布上的drawable对象
a.recycle();
setOrientation(orientation);
}
该属性可以再styles.xml中设置
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:listDivider">@drawable/item_divider
style>
在ondraw方法中,将drawable画出来
主要是根据recyclerview的布局方向,来画横线还是竖线。
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
...
if (mOrientation == VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
布局是竖的,但是画的其实是横向分割线
private void drawVertical(Canvas canvas, RecyclerView parent) {
canvas.save();
final int left;
final int right;
if (parent.getClipToPadding()) {
left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight();
canvas.clipRect(left, parent.getPaddingTop(), right,
parent.getHeight() - parent.getPaddingBottom());
} else {
left = 0;
right = parent.getWidth();
}
//以上是圈定布局的边间,把padding去掉。画横向分割线,需要去掉左右padding。
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
parent.getDecoratedBoundsWithMargins(child, mBounds);
final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));
final int top = bottom - mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
//以上设置drawable的布局边界(左上右下)
mDivider.draw(canvas);
//以上将mdivider画在画布上
}
canvas.restore();//重置画布
}
布局是横的时候,画竖线
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
canvas.save();
final int top;
final int bottom;
if (parent.getClipToPadding()) {
top = parent.getPaddingTop();
bottom = parent.getHeight() - parent.getPaddingBottom();
canvas.clipRect(parent.getPaddingLeft(), top,
parent.getWidth() - parent.getPaddingRight(), bottom);
} else {
top = 0;
bottom = parent.getHeight();
}
//以上是去掉上下padding
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
final int right = mBounds.right + Math.round(ViewCompat.getTranslationX(child));
final int left = right - mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
//设置divider的左上右下边界
mDivider.draw(canvas);
}
canvas.restore();
}
getItemOffsets方法
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
if (mOrientation == VERTICAL) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
outRect.set(左,上,右,下);该方法其实是在设置recyclerview每个item的内padding。分别是左padding,上padding,右padding,下padding
看getItemOffsets方法在何处被调用
Rect getItemDecorInsetsForChild(View child) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.mInsetsDirty) {
return lp.mDecorInsets;
}
final Rect insets = lp.mDecorInsets;
insets.set(0, 0, 0, 0);
final int decorCount = mItemDecorations.size();
for (int i = 0; i < decorCount; i++) {
mTempRect.set(0, 0, 0, 0);
mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
insets.left += mTempRect.left;
insets.top += mTempRect.top;
insets.right += mTempRect.right;
insets.bottom += mTempRect.bottom;
}
lp.mInsetsDirty = false;
return insets;
}
最终在getItemOffsets中设置的左上右下值,被累加到insets中了,insets其实是记录了每个item的总尺寸
最终会在mesure方法中,被用来测量item的尺寸。
public void measureChild(View child, int widthUsed, int heightUsed) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
widthUsed += insets.left + insets.right;
heightUsed += insets.top + insets.bottom;
final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
canScrollHorizontally());
final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
canScrollVertically());
if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
child.measure(widthSpec, heightSpec);
}
}
decoration 的 onDraw,child view 的 onDraw,decoration 的 onDrawOver,这三者依次发生
decoration 的onDraw绘制在最底下,因此其绘制范围任意,但是不在getItemOffsets中的话,会导致overdraw
decoration 的onDrawOver 绘制在最上层
可以利用onDrwaOver 给recyclerview绘制一个蒙版,或者给部分item绘制蒙版
SpanSizeLookup
通常用在grid布局的recyclerview需要添加占一行的header或者footer上
GridLayoutManager gridLayoutManager=new GridLayoutManager(this,2);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return 0;//Returns the number of span occupied by the item at position.
}
});
该布局设置占一行的item
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams){
StaggeredGridLayoutManager.LayoutParams p =(StaggeredGridLayoutManager.LayoutParams) lp;
p.setFullSpan(true);//为StaggeredGridLayoutManager.LayoutParams的特有方法,父类中无。
}
//设置cache缓存的个数
recyclerView.setItemViewCacheSize(3);
//设置recycledViewPool缓存的个数
recyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool() {
@Override
public void setMaxRecycledViews(int viewType, int max) {
super.setMaxRecycledViews(viewType, max);
}
});
//设置CacheExtension
recyclerView.setViewCacheExtension(new RecyclerView.ViewCacheExtension() {
@Override
public View getViewForPositionAndType(RecyclerView.Recycler recycler, int position, int type) {
return null;
}
});
以下是简单实现:
RecyclerView recyclerView = new RecyclerView(this);
final List data = new ArrayList<>();
final QuickRecyclerAdapter adapter = new QuickRecyclerAdapter(data) {
@Override
public View setLayoutView(ViewGroup parent, int viewType) {
return null;
}
@Override
public void setData(BaseViewholder holder, String s, int position) {
}
};
recyclerView.setAdapter(adapter);
ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
Collections.swap(data, from, to);
adapter.notifyItemMoved(from, to);
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int pos = viewHolder.getAdapterPosition();
data.remove(pos);
adapter.notifyItemRemoved(pos);
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
//设置拖拽时候的背景色
viewHolder.itemView.setBackgroundColor(getResources().getColor(R.color.colorAccent));
}
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
//还原背景色
viewHolder.itemView.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
//绑定recyclerview
itemTouchHelper.attachToRecyclerView(recyclerView);
以上拖拽的触发方式是长按,如果要改成触摸的方式,则需要如下:
//定义接口
interface OnStartDragListener{
void startDrag(RecyclerView.ViewHolder holder);
}
//activity实现该接口
public MainActivity extends Activity implements OnStartDragListener{
...
public void startDrag(RecyclerView.ViewHolder holder) {
mHelper.startDrag(holder);
}
}
//如果是对viewholder中的某个控件触摸滑动,如textview
public void onBindViewHolder(ViewHolder holder,int position){
holder.text.setOntouchListener(new View.OnTouchListener(){
@override
public boolean onTouch(View v,MotionEvent event){
if(event.getAction==MotionEvent.ActionDown){
//mListener为实现了OnStartDragListener的activity
mListener.startDrag(holder);
}
}
});
}