之前看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