使用Paging Library获取网络数据

本文介绍Paging Library的基本用法,包括如何配置Gradle依赖、创建数据类和适配器等步骤,并通过实例演示如何实现流畅的滚动加载效果。

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值