table of Contents
-
- Usage of RecyclerView
- Core components in the RecyclerView architecture
- The embodiment of RecyclerView architecture in work
- Architecture thinking of RecyclerView
- The recycling strategy of the recycling pool
- Filling strategy of recycling pool
- Design of recycling pool
- Methods that need to be rewritten
- Reference Code
- Example effect
Usage of RecyclerView
RecyclerView is generally used as a control for the Android display list and has many excellent performances.
1) The recycling pool strategy can load hundreds of millions of data without stalling,
2) The adapter mode can display any display requirements
Core components in the RecyclerView architecture
1. Recycling pool: It can recycle any Item control and return the Item control that matches the type; for example, the first parameter in the onBinderViewHolder method is returned from the recycling pool.
2. Adapter: Adapter interface, often assists RecyclerView to achieve list display; The mode separates the user interface display from the interaction.
3. RecyclerView: It is the interaction of touch events, which mainly realizes the boundary value judgment;
according to the user's touch feedback, it coordinates the work between the recycling pool object and the adapter object.
The embodiment of RecyclerView architecture in work
The working mechanism of the conveyor belt
Loading | transmission | Arrivals | Add |
---|---|---|---|
Vacant position at the beginning of the conveyor belt | Put the goods on the conveyor belt | The conveyor belt starts to drive | Goods arrive at the end of the conveyor belt, put new goods on the conveyor belt |
RecyclerView architecture implementation
load | slide | Slide out | load |
---|---|---|---|
Load the first screen data | The user's finger starts to slide | The user will not need, the information is drawn off the screen | New users at the bottom of the screen, need to see the data |
Architecture thinking of RecyclerView
Architecture: Make full use of the conveyor belt principle, only the data that the user sees will be loaded into the memory, and the unseen data are waiting to be loaded. The conveyor belt can continuously transport billions of goods, and RecyclerView can also display the loading of billions of ltem.
The working mechanism of the conveyor belt can be compared to the producer and consumer model
The recycling strategy of the recycling pool
Filling strategy of recycling pool
Design of recycling pool
Deposit and withdrawal are necessary for the recycling pool strategy
Methods that need to be rewritten
- onMeasure
- onLayout
- onInterceptTouchEvent
- onTouchEvent
- scrollBy
Reference Code
/**
* 1 List<View> viewList
* 缓存已经加载到屏幕上的View这些View不存在回收池中,
* 需要用集合表示,方便后续查找和移除
* 2 nt currentY:记录在Y轴 上滑动的距离
* 3 int rowCount:记录在RecyclerView中加载的总数据条数
* 4 int firstRow;记录在屏幕中第一个View在数据中的位置,比如当前是第34个元
* 素在屏幕的第一个位置
* 5 Recycler recycler:持有一个回收池的引用
* 6 int scrollY: RecyclerView中 第一个View的左上顶点离屏幕的距离
*/
public class RecyclerView extends ViewGroup {
private Adapter adapter;
//当前显示的View
private List<View> viewList;
//当前滑动的y值
private int currentY;
//行数
private int rowCount;
//view的第一行 是占内容的几行
private int firstRow;
//y偏移量
private int scrollY;
//初始化 第一屏最慢
private boolean needRelayout;
private int width;
private int height;
private int[] heights;//item 高度
Recycler recycler;
//最小滑动距离
private int touchSlop;
public Adapter getAdapter() {
return adapter;
}
public void setAdapter(Adapter adapter) {
this.adapter = adapter;
if (adapter != null) {
//回收池初始化
recycler = new Recycler(adapter.getViewTypeCount());
scrollY = 0;
firstRow = 0;
needRelayout = true;
requestLayout();//1 onMeasure 2 onLayout
}
}
public RecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
ViewConfiguration configuration = ViewConfiguration.get(context);
this.touchSlop = configuration.getScaledTouchSlop();
this.viewList = new ArrayList<>();
this.needRelayout = true;
}
//初始化
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (needRelayout || changed) {
needRelayout = false;
viewList.clear();
removeAllViews();
if (adapter != null) {
// 摆放
width = r - l;
height = b - t;
int left, top = 0, right, bottom;
for (int i = 0; i < rowCount && top < height; i++) {
right = width;
bottom = top + heights[i];
// 生成一个View
View view = makeAndStep(i, 0, top, width, bottom);
viewList.add(view);
top = bottom;//循环摆放
}
}
}
}
private View makeAndStep(int row, int left, int top, int right, int bottom) {
View view = obtainView(row, right - left, bottom - top);
view.layout(left, top, right, bottom);
return view;
}
private View obtainView(int row, int width, int height) {
// key type
int itemType = adapter.getItemViewType(row);
// 取不到
View recyclerView = recycler.get(itemType);
View view = null;
if (recyclerView == null) {
view = adapter.onCreateViewHolder(row, recyclerView, this);
if (view == null) {
throw new RuntimeException("onCreateViewHolder 必须填充布局");
}
} else {
view = adapter.onBinderViewHolder(row, recyclerView, this);
}
view.setTag(R.id.tag_type_view, itemType);
view.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)
, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
addView(view, 0);
return view;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int h = 0;
if (adapter != null) {
this.rowCount = adapter.getCount();
heights = new int[rowCount];
for (int i = 0; i < heights.length; i++) {
heights[i] = adapter.getHeight(i);
}
}
// 数据的高度
int tmpH = sumArray(heights, 0, heights.length);
h = Math.min(heightSize, tmpH);
setMeasuredDimension(widthSize, h);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// firstIndex firstIndex+count
private int sumArray(int array[], int firstIndex, int count) {
int sum = 0;
count += firstIndex;
for (int i = firstIndex; i < count; i++) {
sum += array[i];
}
return sum;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercept = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
currentY = (int) event.getRawY();
break;
}
case MotionEvent.ACTION_MOVE: {
int y2 = Math.abs(currentY - (int) event.getRawY());
if (y2 > touchSlop) {
intercept = true;
}
}
}
return intercept;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
// 移动的距离 y方向
int y2 = (int) event.getRawY();
// // 上滑正 下滑负
int diffY = currentY - y2;
currentY = y2;//不加响应会变慢
// 画布移动 并不影响子控件的位置
scrollBy(0, diffY);
}
}
return super.onTouchEvent(event);
}
@Override
public void scrollBy(int x, int y) {
//修正
// scrollY表示 第一个可见Item的左上顶点 距离屏幕的左上顶点的距离
scrollY += y;
scrollY = scrollBounds(scrollY);
// scrolly
if (scrollY > 0) {
// 上滑正 下滑负 边界值
while (scrollY > heights[firstRow]) {
// 1 上滑移除 2 上划加载 3下滑移除 4 下滑加载
removeView(viewList.remove(0));
scrollY -= heights[firstRow];
firstRow++;
}
while (getFillHeight() < height) {
int addLast = firstRow + viewList.size();
View view = obtainView(addLast, width, heights[addLast]);
viewList.add(viewList.size(), view);
}
} else if (scrollY < 0) {
// 4 下滑加载
while (scrollY < 0) {
int firstAddRow = firstRow - 1;
View view = obtainView(firstAddRow, width, heights[firstAddRow]);
viewList.add(0, view);
firstRow--;
scrollY += heights[firstRow + 1];
}
// 3下滑移除
while (sumArray(heights, firstRow, viewList.size()) - scrollY - heights[firstRow + viewList.size() - 1] >= height) {
removeView(viewList.remove(viewList.size() - 1));
}
} else {
}
repositionViews();
}
private int scrollBounds(int scrollY) {
// 上滑
if (scrollY > 0) {
scrollY = Math.min(scrollY, sumArray(heights, firstRow, heights.length - firstRow) - height);
} else {
// 极限值 会取零 非极限值的情况下 socrlly
scrollY = Math.max(scrollY, -sumArray(heights, 0, firstRow));
}
return scrollY;
// 下滑
}
private void repositionViews() {
int left, top, right, bottom, i;
top = -scrollY;
i = firstRow;
for (View view : viewList) {
bottom = top + heights[i++];
view.layout(0, top, width, bottom);
top = bottom;
}
}
private int getFillHeight() {
// 数据的高度 -scrollY
return sumArray(heights, firstRow, viewList.size()) - scrollY;
}
@Override
public void removeView(View view) {
super.removeView(view);
int key = (int) view.getTag(R.id.tag_type_view);
recycler.put(view, key);
}
interface Adapter {
View onCreateViewHolder(int position, View convertView, ViewGroup parent);
View onBinderViewHolder(int position, View convertView, ViewGroup parent);
//Item的类型
int getItemViewType(int row);
//Item的类型数量
int getViewTypeCount();
int getCount();
int getHeight(int index);
}
}
Example effect
Recommended reading
Finally understand why I came to Netease!
Android Development Challenge | Week 2: Countdown Timer
About