![]() |
安静的卤蛋 · 信任开发者有风险吗 - 百度· 3 月前 · |
![]() |
霸气的煎饼 · 《戴手铐的旅客》41年:男主角已经91岁,一 ...· 4 月前 · |
![]() |
侠义非凡的毛衣 · 国产操作系统:统信UOS与银河麒麟系统对比- ...· 5 月前 · |
![]() |
聪明的小狗 · 西藏将铸牢中华民族共同体意识融入办学治校、教 ...· 6 月前 · |
![]() |
好帅的大蒜 · 我晕,HongkongDoll上脚AJ1&a ...· 9 月前 · |
主要是在使用 RecyclerView 过程中遇到的细碎问题和解决方案。
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 如果可以确定每个 item 的高度是固定的,设置这个选项可以提高性能
recyclerView.setHasFixedSize(true);
// item 显示的动画
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(new MyAdapter());
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
public List<String> datas;
public MyAdapter(List<String> datas) {
this.datas = datas;
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new ViewHolder(view);
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.mTextView.setText(datas.get(position));
@Override
public int getItemCount() {
return datas.size();
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View view){
super(view);
mTextView = (TextView) view.findViewById(R.id.text);
}
public class MyAdapter extends RecyclerView.Adapter {
public List<String> datas;
public MyAdapter(List<String> datas) {
this.datas = datas;
public enum ITEM_TYPE {
ITEM1, ITEM2
@Override
public int getItemViewType(int position) {
// Enum 类提供了一个 ordinal() 方法,返回枚举类型的序数
// ITEM_TYPE.ITEM1.ordinal() 代表0, ITEM_TYPE.ITEM2.ordinal() 代表1
return position % 2 == 0 ? ITEM_TYPE.ITEM1.ordinal() : ITEM_TYPE.ITEM2.ordinal();
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 根据不同 viewType 加载不同的布局
if (viewType == ITEM_TYPE.ITEM1.ordinal()) {
View view1 = LayoutInflater.from(parent.getContext()).inflate(R.layout.item1, parent, false);
return new ViewHolder1(view1);
} else if (viewType == ITEM_TYPE.ITEM2.ordinal()) {
View view2 = LayoutInflater.from(parent.getContext()).inflate(R.layout.item2, parent, false);
return new ViewHolder2(view2);
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (holder instanceof ViewHolder1) {
((ViewHolder1) holder).mTextView.setText(datas.get(position));
} else if (holder instanceof ViewHolder2) {
((ViewHolder2) holder).mTextView.setText(datas.get(position)+"abc");
@Override
public int getItemCount() {
return datas.size();
public static class ViewHolder1 extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View view){
super(view);
mTextView = (TextView) view.findViewById(R.id.text);
public static class ViewHolder2 extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View view){
super(view);
mTextView = (TextView) view.findViewById(R.id.big_text);
}
定义分隔线 divider.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#7b7a7a"/>
<size android:height="1dp"/>
</shape>
自定义类继承 RecyclerView.ItemDecoration,重写回调方法
// 线性布局用
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
public class MyDecoration extends RecyclerView.ItemDecoration{
private Context mContext;
private Drawable mDivider;
private int mOrientation;
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
public MyDecoration(Context context, int orientation) {
this.mContext = context;
mDivider = context.getResources().getDrawable(R.drawable.divider);
setOrientation(orientation);
// 设置屏幕的方向
public void setOrientation(int orientation){
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){
throw new IllegalArgumentException("invalid orientation");
mOrientation = orientation;
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == HORIZONTAL_LIST){
drawVerticalLine(c, parent, state);
}else {
drawHorizontalLine(c, parent, state);
// 画竖线, 这里的 parent 其实是显示在屏幕显示的这部分
public void drawVerticalLine(Canvas c, RecyclerView parent, RecyclerView.State state){
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++){
final View child = parent.getChildAt(i);
// 获得 child 的布局信息
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)child.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
// 画横线
public void drawHorizontalLine(Canvas c, RecyclerView parent, RecyclerView.State state){
int top = parent.getPaddingTop();
int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++){
final View child = parent.getChildAt(i);
// 获得 child 的布局信息
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)child.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
// 由于 Divider 也有长宽高,每一个 Item 需要向下或者向右偏移
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if(mOrientation == HORIZONTAL_LIST){
// 画竖线,就是往右偏移一个分割线的宽度
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else {
// 画横线,就是往下偏移一个分割线的高度
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
}
// 网格或瀑布流布局用
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[] { android.R.attr.listDivider };
private Drawable mDivider;
public DividerGridItemDecoration(Context context) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
@Override
public void onDraw(Canvas c, RecyclerView parent, State state) {
drawHorizontal(c, parent);
drawVertical(c, parent);
private int getSpanCount(RecyclerView parent) {
// 列数
int spanCount = -1;
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
return spanCount;
public void drawHorizontal(Canvas c, RecyclerView parent) {
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 left = child.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin
+ mDivider.getIntrinsicWidth();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
public void drawVertical(Canvas c, RecyclerView parent) {
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.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) {
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
if ((pos + 1) % spanCount == 0) { // 如果是最后一列,则不需要绘制右边
return true;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
if ((pos + 1) % spanCount == 0) { // 如果是最后一列,则不需要绘制右边
return true;
} else {
childCount = childCount - childCount % spanCount;
if (pos >= childCount) // 如果是最后一列,则不需要绘制右边
return true;
return false;
private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) {
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
childCount = childCount - childCount % spanCount;
if (pos >= childCount) // 如果是最后一行,则不需要绘制底部
return true;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
// StaggeredGridLayoutManager 且纵向滚动
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
childCount = childCount - childCount % spanCount;
// 如果是最后一行,则不需要绘制底部
if (pos >= childCount)
return true;
} else { // StaggeredGridLayoutManager 且横向滚动
// 如果是最后一行,则不需要绘制底部
if ((pos + 1) % spanCount == 0) {
return true;
return false;
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
if (isLastRaw(parent, itemPosition, spanCount, childCount)) { // 如果是最后一行,则不需要绘制底部
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount)) { // 如果是最后一列,则不需要绘制右边
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
}
使用自定义分隔线:
mRecyclerView.addItemDecoration(new MyDecoration(this, MyDecoration.VERTICAL_LIST));
mRecyclerView.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.HORIZONTAL));
可以更改自带分隔线的样式:
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:listDivider">@drawable/divider_bg</item>
</style>
可以自己画个分隔线 shape。
给 item 设置一个 selector,设置
android:state_focused
不同时不同的背景,关键是要在 item 根布局设置
android:focusable="true"
android:clickable="true"
android:focusableInTouchMode="true"
EditText 添加 TextWatcher 后,每次执行刷新、添加数据之类的操作,即只要执行 onBindViewHolder 就会进入监听,导致数据错乱。
解决方法时一开始先移除监听:
holder.tvOrderGoodsQuantity.removeTextChangedListener((QunaitityWatcher) holder.tvOrderGoodsQuantity.getTag());
然后做完数据操作后,再添加:
QunaitityWatcher watcher = new QunaitityWatcher(holder.tvOrderGoodsQuantity, holder.tvOrderGoodsQuantity, mData, holder.tvOrderGoodsAmount);
holder.tvOrderGoodsQuantity.addTextChangedListener(watcher);
自定义 TextWatcher,把一些 View 作为参数传入,以免数据错乱,不然可能会传到其它 item 的 View 上。
private class QunaitityWatcher implements TextWatcher {
private EditText editText;
private GoodModel model;
private TextView tv;
private EditText et;
public QunaitityWatcher(EditText et, EditText editText, GoodModel model, TextView tv) {
this.et = et;
this.editText = editText;
this.model = model;
this.tv = tv;
et.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus)
et.setText(Utils.subZeroAndDot(model.getScatterGoodQuantity()));
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
@Override
public void afterTextChanged(Editable s) {
String text = s.toString();
if (!TextUtils.isEmpty(text) && Utils.isDouble(text) && !text.startsWith(".")) {
double d = Double.parseDouble(text);
if (d <= 0) {
editText.setText("1"); // 默认的 1 件
d = 1;
} else {
int dotindex = text.indexOf(".");
if (dotindex > 0 && text.length() > dotindex + 3) {
s.delete(dotindex + 3, text.length());
} else {
d = Double.parseDouble(s.toString());
tv.setText("¥" + Utils.subZeroAndDot(d * model.getPrice()));
model.setScatterGoodQuantity(d);
EventBus.getDefault().post(new ChangeGoodQuantityEvent());
refreshQuantityAndPrice();
}
因为 EditText 默认 FocusableInTouchMode 为 false,需设置
et.setFocusableInTouchMode(true);
et.requestFocus();
方法一:
public static boolean isVisBottom(RecyclerView recyclerView){
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
// 屏幕中最后一个可见子项的 position
int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
// 当前屏幕所看到的子项个数
int visibleItemCount = layoutManager.getChildCount();
// 当前 RecyclerView 的所有子项个数
int totalItemCount = layoutManager.getItemCount();
// RecyclerView 的滑动状态
int state = recyclerView.getScrollState();
if(visibleItemCount > 0 && lastVisibleItemPosition == totalItemCount - 1 && state == recyclerView.SCROLL_STATE_IDLE){
return true;
} else {
return false;
}
方法二:
public static boolean isSlideToBottom(RecyclerView recyclerView) {
if (recyclerView == null) return false;
if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
>= recyclerView.computeVerticalScrollRange())
return true;
return false;
}
computeVerticalScrollExtent()
是当前屏幕显示的区域高度,
computeVerticalScrollOffset()
是当前屏幕之前滑过的距离,而
computeVerticalScrollRange()
是整个 View 控件的高度。
方法三:
复杂的不规则列(有的行显示的列数多,有的行显示的列数少,并且每列显示的内容页不一样),使用 GridLayoutManager.SpanSizeLookup 的相关功能实现,新建 GridLayoutManager 的时候列数填写所有可能列数的最小公倍数。再结合 adapter 中的:
GridLayoutManager layoutManager = new GridLayoutManager(getActivity(), 2);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
// 设置对应 position 位置的 item 的跨列数,比如第一行占两列,其他行每个 Item 占一列
return position == 0 ? 2 : 1;
recyclerView.setLayoutManager(layoutManager);
Android 24.2.0 的 support 包中添加了 SnapHelper,SnapHelper 是对 RecyclerView 的拓展,旨在支持 RecyclerView 的对齐方式,也就是通过计算对齐 RecyclerView 中 TargetView 的指定点或者容器中的任何像素点。
SnapHelper 继承自 RecyclerView.OnFlingListener,并实现抽象方法 onFling,支持 SnapHelper 的 RecyclerView.LayoutManager 必须实现 RecyclerView.SmoothScroller.ScrollVectorProvider 接口,或者自己实现 onFling 方法手动处理。SnapHelper 有以下几个重要方法:
SnapHelper 是一个抽象类,要使用 SnapHelper,需要实现它的几个方法。而 Google 内置了两个默认实现类,LinearSnapHelper 和 PagerSnapHelper,LinearSnapHelper 可以使 RecyclerView 的当前 Item 居中显示(横向和竖向都支持),PagerSnapHelper 使 RecyclerView 像 ViewPager 一样,每次只能滑动一页(LinearSnapHelper 支持快速滑动),PagerSnapHelper 也是 Item 居中对齐。
LinearSnapHelper 使当前 Item 居中显示,并且显示前一页和后一页的部分。常用场景是横向的 RecyclerView,类似 ViewPager 效果,但是又可以快速滑动(滑动多页)。
LinearLayoutManager manager = new LinearLayoutManager(getContext());
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
mRecyclerView.setLayoutManager(manager);
// 将 SnapHelper attach 到 RecyclrView
LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(mRecyclerView);
PagerSnapHelper 是 Android 25.1.0 support 包加入的,展示效果和 LineSnapHelper 一样,只是 PagerSnapHelper 限制一次只能滑动一页,不能快速滑动。
PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(mRecyclerView);
通过上面三个方法就实现了 SnapHelper 的对齐,只是有几个抽象方法是没有实现的,具体的对齐规则交给子类去实现。
下面看一下 LinearSnapHelper 是怎么实现居中对齐的。
更改 LinearSnapHelper 的对齐规则,更改为开始对齐(计算目标 View 到 Parent start 要滑动的距离),其他的逻辑和 LinearSnapHelper 一样的。
public class StartSnapHelper extends LinearSnapHelper {
private OrientationHelper mHorizontalHelper, mVerticalHelper;
@Nullable
@Override
public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) {
int[] out = new int[2];
if (layoutManager.canScrollHorizontally()) {
// 不再是 distanceToCenter
out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager));
} else {
out[0] = 0;
if (layoutManager.canScrollVertically()) {
out[1] = distanceToStart(targetView, getVerticalHelper(layoutManager));
} else {
out[1] = 0;
return out;
private int distanceToStart(View targetView, OrientationHelper helper) {
return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding();
@Nullable
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof LinearLayoutManager) {
if (layoutManager.canScrollHorizontally()) {
return findStartView(layoutManager, getHorizontalHelper(layoutManager));
} else {
return findStartView(layoutManager, getVerticalHelper(layoutManager));
return super.findSnapView(layoutManager);
private View findStartView(RecyclerView.LayoutManager layoutManager,
OrientationHelper helper) {
if (layoutManager instanceof LinearLayoutManager) {
int firstChild = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
// 需要判断是否是最后一个 Item,如果是最后一个则不让对齐,以免出现最后一个显示不完全。
boolean isLastItem = ((LinearLayoutManager) layoutManager)
.findLastCompletelyVisibleItemPosition()
== layoutManager.getItemCount() - 1;
if (firstChild == RecyclerView.NO_POSITION || isLastItem) {
return null;
View child = layoutManager.findViewByPosition(firstChild);
if (helper.getDecoratedEnd(child) >= helper.getDecoratedMeasurement(child) / 2
&& helper.getDecoratedEnd(child) > 0) {
return child;
} else {
if (((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition()
== layoutManager.getItemCount() - 1) {
return null;
} else {
return layoutManager.findViewByPosition(firstChild + 1);
return super.findSnapView(layoutManager);
private OrientationHelper getHorizontalHelper(
@NonNull RecyclerView.LayoutManager layoutManager) {
if (mHorizontalHelper == null) {
mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
return mHorizontalHelper;
private OrientationHelper getVerticalHelper(RecyclerView.LayoutManager layoutManager) {
if (mVerticalHelper == null) {
mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
return mVerticalHelper;
}
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setSmoothScrollbarEnabled(true);
layoutManager.setAutoMeasureEnabled(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.setNestedScrollingEnabled(false);
notifyDataSetChanged 是全量的刷新,且无法应用 ItemAnimator,而 notifyItemXXX 之类的方法使用场景有限,不适合整体的数据更新。
support-v7:24.2.0 中新增了工具类 DiffUtil,用来比较两个数据集,寻找出旧数据集/新数据集的最小变化量。
它会自动计算新老数据集的差异,并根据差异情况,自动调用 notifyItemXXX 之类的方法。
基本用法:
进阶用法:
onBindViewHolder(VH holder, int position, List<Object> payloads)
。
如果 payloads 不为空,那么当前绑定了旧数据的 ViewHolder 和 Adapter 使用 payload 进行局部更新。如果 payload 为空,Adapter 则进行一次完整的更新(调用两参方法)。
payloads 对象不会为 null,但可能是 empty,所以需要判断一下。
@Override public void onBindViewHolder(DiffVH holder, int position, List<Object> payloads) { if (payloads.isEmpty()) { // 完整更新 onBindViewHolder(holder, position); } else { // 局部更新 // 取出 getChangePayload 方法里返回的 payload Bundle payload = (Bundle) payloads.get(0); TestBean bean = mDatas.get(position); for (String key : payload.keySet()) { switch (key) { case "KEY_DESC": // 可以用 payload 里的数据,不过 data 也是新的,也可以用 holder.tv2.setText(bean.getDesc()); break; case "KEY_PIC": holder.iv.setImageResource(payload.getInt(key)); break; default: break; } } } }
添加依赖
implementation "android.arch.paging:runtime:1.0.1"
数据源,可以从网络获取或从本地获取要显示的数据。
DataSource 有三个抽象子类 ItemKeyedDataSource、PageKeyedDataSource、PositionalDataSource,它们分别有一个子类 WrapperItemKeyedDataSource、WrapperPageKeyedDataSource、WrapperPositionalDataSource。
ItemKeyedDataSource<Key, Value>
:适用于目标数据的加载依赖特定 item 的信息,比如需要根据第 N 项的信息加载第 N+1 项的数据,Key 中包含了第 N 项的信息。
PageKeyedDataSource<Key, Value>
:适用于目标数据根据页信息请求数据的场景,即 Key 字段是页相关的信息,而不是前一个 item 的信息。
PositionalDataSource<T>
:适用于目标数据总数固定,通过特定的位置加载数据的情况,T 是 Value,隐含的 Key 就是位置信息。
以 Wrap 开头的三个类,从名字和源码可以看出就是一个装饰,构造方法传入被装饰的那个类和一个 Function,除了加载数据的方法外都是直接委托给被装饰类,加载数据的方法将 Value 经过 Function 进行一种转换再调用被装饰类的方法。
class MyDataSource : PageKeyedDataSource<Int, String>() {
// 开始加载的数据
override fun loadInitial(params: PageKeyedDataSource.LoadInitialParams<Int>, callback: PageKeyedDataSource.LoadInitialCallback<Int, String>) {
// 没有前一页,所以第二个参数为 null,第三个参数指定下一页的 key
callback.onResult(getData(0, params.requestedLoadSize), null, 1)
// 加载上一页的数据
override fun loadBefore(params: PageKeyedDataSource.LoadParams<Int>, callback: PageKeyedDataSource.LoadCallback<Int, String>) {
callback.onResult(getData(params.key, params.requestedLoadSize), params.key - 1)
// 加载下一页的数据
override fun loadAfter(params: PageKeyedDataSource.LoadParams<Int>, callback: PageKeyedDataSource.LoadCallback<Int, String>) {
callback.onResult(getData(params.key, params.requestedLoadSize), params.key + 1)
private fun getData(page: Int, size: Int): List<String> {
val data = ArrayList<String>()
(0..size).forEach {
data.add("page $page item $it")
return data
}
loadInitial() 方法的第一个参数 LoadInitialParams 内有两个属性:
LoadParams 的 key 属性在这里就代表页,也是 callback.onResult 的最后一个参数传进去的。
负责从 DataSource 取出数据,第一次显示多少数据,之后每次加载多少数据。
class MyDataSourceFactory : DataSource.Factory<Int, String>() {
// 通过工厂生产一个数据源对象
override fun create(): DataSource<Int, String> = MyDataSource()
}
val data = LivePagedListBuilder(MyDataSourceFactory(), PagedList.Config.Builder()
.setPageSize(10) // 每页 10 条
.setEnablePlaceholders(true)
.setInitialLoadSizeHint(20) // 最初加载 20 条
.build()).build()
PagedList.Config.Builder 里有四个属性:
class MyViewHolder(val view : View) : RecyclerView.ViewHolder(view) {
val tv : TextView = view as TextView
class MyPagingAdapter : PagedListAdapter<String, MyViewHolder>(DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val tv = TextView(parent.context)
tv.textColor = Color.BLACK
tv.textSize = 12f
return MyViewHolder(tv)
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
// getItem 获取字符串
holder.tv.text = getItem(position)
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(oldConcert: String, newConcert: String) = oldConcert == newConcert
override fun areContentsTheSame(oldConcert: String, newConcert: String) = oldConcert == newConcert
}
recyclerView.layoutManager = LinearLayoutManager(this)
val adapter = MyPagingAdapter()
recyclerView.addItemDecoration(DividerItemDecoration(ctx, DividerItemDecoration.VERTICAL))
recyclerView.adapter = adapter
val data = LivePagedListBuilder(MyDataSourceFactory(),
PagedList.Config.Builder()
.setPageSize(10) // 每页 10 条
.setEnablePlaceholders(true)
.setInitialLoadSizeHint(20) // 最初加载 20 条
![]() |
安静的卤蛋 · 信任开发者有风险吗 - 百度 3 月前 |