概述
RecyclerView
是 Android 中用于高效显示大量数据的视图组件,它是 ListView 的升级版本,支持更灵活的布局和功能。
我们创建一个RecyclerView的Adapter:
public class MyRecyclerView extends RecyclerView.Adapter<MyRecyclerView.MyHolder> {
private List<String> strings;
private Context context;
public MyRecyclerView(List<String> strings, Context context) {
this.strings = strings;
this.context = context;
}
@NonNull
@Override
public MyRecyclerView.MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
MyRecyclerView.MyHolder viewHolder = new MyHolder(view);
Log.d("MyRecyclerView", "onCreateViewHolder: ");
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull MyRecyclerView.MyHolder holder, int position) {
holder.textView.setText("第" + position + "项");
Log.d("MyRecyclerView", "onBindViewHolder: " + position);
}
@Override
public int getItemCount() {
return strings == null ? 0 : strings.size();
}
public class MyHolder extends RecyclerView.ViewHolder {
private TextView textView;
public MyHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(android.R.id.text1);
}
}
}
我们在onCreateViewHolder和onBindViewHolder都打印log。
onCreateViewHolder()
会在创建一个新view的时候调用,onBindViewHolder()
会在已存在view,绑定数据的时候调用。
我们来看一下运行时打印的log:
在最开始加载view的时候,两个方法onCreateViewHolder()
和onBindViewHolder()
都执行了,但是当我们上下滑动RecyclerView的时候,我们会发现只执行了onBindViewHolder()
方法。所以说,RecyclerView并不是会一直重新创建View,而是会对view进行复用。
复用机制
当我们想去通过看源码去了解缓存复用机制的时候,我们要去想看源码的入口在哪里。上文我们提到是在滑动RecyclerView的时候进行了缓存复用,所以我们会想到去看 onTouchEvent
这个方法:
@Override
public boolean onTouchEvent(MotionEvent e) {
...
case MotionEvent.ACTION_MOVE: {
final int index = e.findPointerIndex(mScrollPointerId);
if (index < 0) {
Log.e(TAG, "Error processing scroll; pointer index for id "
+ mScrollPointerId + " not found. Did any MotionEvents get skipped?");
return false;
}
final int x = (int) (e.getX(index) + 0.5f);
final int y = (int) (e.getY(index) + 0.5f);
int dx = mLastTouchX - x;
int dy = mLastTouchY - y;
if (mScrollState != SCROLL_STATE_DRAGGING) {
boolean startScroll = false;
if (canScrollHorizontally) {
if (dx > 0) {
dx = Math.max(0, dx - mTouchSlop);
} else {
dx = Math.min(0, dx + mTouchSlop);
}
if (dx != 0) {
startScroll = true;
}
}
if (canScrollVertically) {
if (dy > 0) {
dy = Math.max(0, dy - mTouchSlop);
} else {
dy = Math.min(0, dy + mTouchSlop);
}
if (dy != 0) {
startScroll = true;
}
}
if (startScroll) {
setScrollState(SCROLL_STATE_DRAGGING);
}
}
if (mScrollState == SCROLL_STATE_DRAGGING) {
mReusableIntPair[0] = 0;
mReusableIntPair[1] = 0;
if (dispatchNestedPreScroll(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
mReusableIntPair, mScrollOffset, TYPE_TOUCH
)) {
dx -= mReusableIntPair[0];
dy -= mReusableIntPair[1];
// Updated the nested offsets
mNestedOffsets[0] += mScrollOffset[0];
mNestedOffsets[1] += mScrollOffset[1];
// Scroll has initiated, prevent parents from intercepting
getParent().requestDisallowInterceptTouchEvent(true);
}
mLastTouchX = x - mScrollOffset[0];
mLastTouchY = y - mScrollOffset[1];
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
e)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
if (mGapWorker !=