之前看architecture时候,看见了paging library这个库,因为谷歌给出的案例是配合Room使用,所以一直没去深究,今天偶然看见了别人的博客,发现愿挨还可以配合网络数据使用,所以今天就来趴一趴用法.
首先放上官网地址:https://developer.android.google.cn/topic/libraries/architecture/paging.html,
还有个中译:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0920/8533.html
然后就是kotlin的:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0922/8539.html
现在开始,首先配置好gradle,在标准的lifecycle基础上增加paging库,这是我的 gradle配置:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation "android.arch.lifecycle:extensions:1.0.0"
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.android.support:recyclerview-v7:26.1.0'
annotationProcessor "android.arch.lifecycle:compiler:1.0.0"
implementation "android.arch.paging:runtime:1.0.0-alpha3"
implementation 'com.github.bumptech.glide:glide:4.3.1'
}复制代码
然后现在是]我们用来配合recyclerview使用:
新建MainActivity,首先写布局,布局很简单,就是个recyclerView
activity_main.xml:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.yzl.gank.pagetest.ui.mian.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>复制代码
然后开始写data数据类
MainData:
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.paging.DataSource;
import android.arch.paging.LivePagedListProvider;
import android.arch.paging.PagedList;
import android.arch.paging.TiledDataSource;
import android.support.annotation.NonNull;
import com.yzl.gank.pagetest.bean.GankData;
import com.yzl.gank.pagetest.http.AppService;
import com.yzl.gank.pagetest.http.BaseResponse;
import com.yzl.gank.pagetest.http.RetrofitApi;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Response;
/**
* Created by 10488 on 2017-11-11.
*/
public class MainData extends AndroidViewModel {
/**
* 每次需要10个数据.
*/
private static final int NEED_NUMBER = 10;
/**
* 福利第一页.
*/
private static final int PAGE_FIRST = 1;
/**
* 分页.
*/
private int mPage = PAGE_FIRST;
/**
* 列表数据.
*/
private LiveData<PagedList<GankData>> mDataLiveData;
public MainData(@NonNull Application application) {
super(application);
}
public LiveData<PagedList<GankData>> getDataLiveData() {
initPageList();
return mDataLiveData;
}
/**
* 初始化pageList.
*/
private void initPageList() {
//获取dataSource,列表数据都从这里获取,
final TiledDataSource<GankData> tiledDataSource = new TiledDataSource<GankData>() {
/**
* 需要的总个数,如果数量不定,就传COUNT_UNDEFINED.
*/
@Override
public int countItems() {
return DataSource.COUNT_UNDEFINED;
}
/**
* 返回需要加载的数据.
* 这里是在线程异步中执行的,所以我们可以同步请求数据并且返回
* @param startPosition 现在第几个数据
* @param count 加载的数据数量
*/
@Override
public List<GankData> loadRange(int startPosition, int count) {
List<GankData> gankDataList = new ArrayList<>();
//这里我们用retrofit获取数据,每次获取十条数据,数量不为空,则让mPage+1
try {
Response<BaseResponse<List<GankData>>> execute = RetrofitApi.getInstance().mRetrofit.create(AppService.class)
.getWelfare1(mPage, NEED_NUMBER).execute();
gankDataList.addAll(execute.body().getResults());
if (!gankDataList.isEmpty()) {
mPage++;
}
} catch (IOException e) {
e.printStackTrace();
}
return gankDataList;
}
};
//这里我们创建LiveData<PagedList<GankData>>数据,
mDataLiveData = new LivePagedListProvider<Integer, GankData>() {
@Override
protected DataSource<Integer, GankData> createDataSource() {
return tiledDataSource;
}
}.create(0, new PagedList.Config.Builder()
.setPageSize(NEED_NUMBER) //每次加载的数据数量
//距离本页数据几个时候开始加载下一页数据(例如现在加载10个数据,设置prefetchDistance为2,则滑到第八个数据时候开始加载下一页数据).
.setPrefetchDistance(NEED_NUMBER)
//这里设置是否设置PagedList中的占位符,如果设置为true,我们的数据数量必须固定,由于网络数据数量不固定,所以设置false.
.setEnablePlaceholders(false)
.build());
}
}复制代码
这里我们需要创建pageList的实例,pageList中有create方法
PageList:
@NonNull
private static <K, T> PagedList<T> create(@NonNull DataSource<K, T> dataSource,
@NonNull Executor mainThreadExecutor,
@NonNull Executor backgroundThreadExecutor,
@NonNull Config config,
@Nullable K key) {
if (dataSource.isContiguous() || !config.mEnablePlaceholders) {
if (!dataSource.isContiguous()) {
//noinspection unchecked
dataSource = (DataSource<K, T>) ((TiledDataSource<T>) dataSource).getAsContiguous();
}
ContiguousDataSource<K, T> contigDataSource = (ContiguousDataSource<K, T>) dataSource;
return new ContiguousPagedList<>(contigDataSource,
mainThreadExecutor,
backgroundThreadExecutor,
config,
key);
} else {
return new TiledPagedList<>((TiledDataSource<T>) dataSource,
mainThreadExecutor,
backgroundThreadExecutor,
config,
(key != null) ? (Integer) key : 0);
}
}复制代码
可见我们需要四个数据,但是LivePagedListProvider帮我们默认配置了,现在我们只是修改下我们需要的配置.
接下来看DataSource,DataSourece上有注释说明,大概意思就是叫我们按需求继承KeyedDataSource或者TiledDataSource,目前我们先用TiledDataSource,KeyedDataSource几个 方法在网络获取数据有点多余.
由于我们设置了PlaceHolders为false,所以我们最终的DataSource是ContiguousDataSource.
数据这边我们就设置完了,接下来看Activity.
首先我们先来看RecyclerView需要的adapter:
MyAdapter:
import android.arch.paging.PagedListAdapter;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.recyclerview.extensions.DiffCallback;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.yzl.gank.pagetest.R;
import com.yzl.gank.pagetest.bean.GankData;
/**
* Created by 10488 on 2017-11-11.
*/
public class MyAdapter extends PagedListAdapter<GankData, BaseViewHolder> {
private Context mContext;
public MyAdapter(@NonNull DiffCallback<GankData> diffCallback) {
super(diffCallback);
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mContext = parent.getContext();
return new BaseViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_main, parent, false));
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
TextView tvName = holder.getView(R.id.tv_name);
ImageView imageView = holder.getView(R.id.iv);
GankData data = getItem(position);
tvName.setText(data.getCreatedAt());
Glide.with(mContext).load(data.getUrl()).into(imageView);
}
}复制代码
这里我们需要使用库提供的ListAdapter,其他写法和普通的adapter一致,注意构造方法需要我们传入DiffCallBack,是不是想到了之前RecyclerView的 工具类DiffUtil呢(如果不知道这个类的话,该去看看别人的博客补下知识了).当然这个diffUtil不是recyclerView那个,这个是page库自带的,
我们来看下DiffCallBack:
DiffCallBack:
public abstract class DiffCallback<T> {
/**
* Called to decide whether two objects represent the same item.
*
* @param oldItem The item in the old list.
* @param newItem The item in the new list.
* @return True if the two items represent the same object or false if they are different.
* @see android.support.v7.util.DiffUtil.Callback#areItemsTheSame(int, int)
*/
public abstract boolean areItemsTheSame(@NonNull T oldItem, @NonNull T newItem);
/**
* Called to decide whether two items have the same data. This information is used to detect if
* the contents of an item have changed.
*
* @param oldItem The item in the old list.
* @param newItem The item in the new list.
* @return True if the contents of the items are the same or false if they are different.
* @see android.support.v7.util.DiffUtil.Callback#areContentsTheSame(int, int)
*/
public abstract boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem);
/**
* Called to get a change payload between an old and new version of an item.
*
* @see android.support.v7.util.DiffUtil.Callback#getChangePayload(int, int)
*/
@SuppressWarnings("WeakerAccess")
public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) {
return null;
}
}复制代码
好吧,除了少了两个get方法,和DiffUtil的CallBack一模一样.
好了,现在来看我们的activity
MainActivity:
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.arch.paging.PagedList;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.recyclerview.extensions.DiffCallback;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import com.yzl.gank.pagetest.R;
import com.yzl.gank.pagetest.adapter.MyAdapter;
import com.yzl.gank.pagetest.bean.GankData;
public class MainActivity extends AppCompatActivity {
private RecyclerView mRv;
private MainData mMainData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMainData = ViewModelProviders.of(this).get(MainData.class);
initView();
initList();
}
private void initView() {
mRv = findViewById(R.id.rv);
}
private void initList() {
final MyAdapter myAdapter = new MyAdapter(new DiffCallback<GankData>() {
@Override
public boolean areItemsTheSame(@NonNull GankData oldUser, @NonNull GankData newUser) {
return oldUser.get_id().equals(newUser.get_id());
}
@Override
public boolean areContentsTheSame(@NonNull GankData oldUser, @NonNull GankData newUser) {
return oldUser.getUrl().equals(newUser.getUrl());
}
});
mRv.setAdapter(myAdapter);
mRv.setLayoutManager(new LinearLayoutManager(this));
mMainData.getDataLiveData().observe(this, new Observer<PagedList<GankData>>() {
@Override
public void onChanged(@Nullable PagedList<GankData> gankData) {
myAdapter.setList(gankData);
}
});
}
}复制代码
每次observer回调时候执行myAdapter.setList(gankData),然后会去计算新旧数据的不同点,进行adapter的刷新.
好了,基础用法就结束了,来看看运行效果,虽然从网络获取数据,但是滑动完全无感知加载更多.
至于更多的用法就要看怎么去探索了
本文只是pageLibrary库对于网络的简单使用,目前该库还处于测试阶段,所以不适合加入到正式项目中,所以大家可以在自己demo中写下,顺便看看源码,看看谷歌会给我们怎样的设计思路.
最后放上文章demo地址:https://github.com/a1048823898/page_tes