目录
RecyclerView的用法
RecyclerView一般作为Android显示列表的控件,有诸多优异的性能。
1)回收池策略能加载上亿级数据不发生卡顿,
2)适配器模式能展示任意显示需求
RecyclerView架构中核心组件
1、回收池:能回收任意Item控件,并返回符合类型的Item控件; 比如onBinderViewHolder方法中的第一个参数是从回收池中返回的
2、适配器: Adapter接口,经常辅助RecyclerView实现列表展示; 适配器模式将用户界面展示与交互分离
3、RecyclerView: 是做触摸事件的交互,主要实现边界值判断;
根据用户的触摸反馈,协调回收池对象与适配器对象之间的工作。
RecyclerView架构在工作中的体现
传送带的工作机制
上货 | 传动 | 到达 | 新增 |
---|---|---|---|
传送带的始端空出位置 | 将货物放入传送带 | 传送带开始传动 | 货物到达传送带终点,将新的货物放入传送带 |
RecyclerView架构实现
加载 | 滑动 | 滑出 | 加载 |
---|---|---|---|
加载第一屏数据 | 用户手指开始滑动 | 用户将不需要的,信息划出屏幕 | 幕底端新增用户,需要看到的数据 |
RecyclerView的架构思考
架构:充分利用传送带原理,只有用户看到的数据才会加载到内存,看不到的在等待被加载。传送带能够源源不断地传送亿级货物,RecyclerView也能够显示加载亿级ltem。
传送带的工作机制可以比作生产者与消费者模式
回收池的回收策略
回收池的填充策略
回收池的设计
存和取是回收池策略必须实现的
需要重写的方法
- onMeasure
- onLayout
- onInterceptTouchEvent
- onTouchEvent
- scrollBy
参考代码
/**
* 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);
}
}
示例效果
推荐阅读
About