Android Jetpack套件之Paging分頁組件

Google 2018 I/O大會上,Google正式推出了AndroidJetpack ——這是一套組件、工具和指導,可以幫助開發者構建出色的 Android 應用。
架構組件可幫助您設計穩健、可測試且易維護的應用。

  • 數據綁定

    以聲明方式將可觀察數據綁定到界面元素

  • Lifecycles

    管理您的 Activity 和 Fragment 生命週期

  • LiveData

    在底層數據庫更改時通知視圖

  • Navigation

    處理應用內導航所需的一切

  • Paging

    逐步從您的數據源按需加載信息

  • Room

    流暢地訪問 SQLite 數據庫

  • ViewModel

    以注重生命週期的方式管理界面相關的數據

而今天我們要將的是

 Paging分頁組件

有英文閱讀能力,並且已經掌握了Kotlin的可以通過下面鏈接瀏覽官方文檔,畢竟官方的纔是最接近真理的:https://developer.android.google.cn/topic/libraries/architecture/paging/

喜歡簡單一點的,我只想先學會簡單地訪問網絡加載數據,怎麼辦?那你找對地方了,功能沒實現,你說得天花亂墜都是瞎扯淡。那我們一步一步來吧!

引入庫

3個都可以實現paging,這裏爲了簡單,就不去涉及rxjava了,我們引入runtime版本(如果不需要調試功能的,可以使用common版本,博主使用過common版本,總覺得少點什麼東西,後來就換回runtime版本)


dependencies {
    //...
    implementation 'android.arch.paging:runtime:1.0.1'
    //...

}

實現分頁加載功能

 

爲了方便大家理解,我將它們分成兩個部分:

  • 準備數據部分
  • 綁定UI部分

準備數據部分

1、定義DataSource,這裏放關鍵的加載數據邏輯

/*
    //step8、定製自己的DataSource,繼承哪個DataSource是根據後臺接口來定的,這裏的接口是pageCount和pageSize
    所以,我們需要繼承PositionalDataSource<T>,並且實現他們的抽象函數
    */
    class MyDataSource extends PositionalDataSource<MusicBean>{
        List<MusicBean> listData = new ArrayList<>();
        /**
         * recyclerView第一次加載時自動調用
         *
         * @param params   包含當前加載的位置position、下一頁加載的長度count
         * @param callback 將數據回調給UI界面使用callback.onResult
         */
        @Override
        public void loadInitial(@NonNull LoadInitialParams params, @NonNull final LoadInitialCallback<MusicBean> callback) {
            //step9、在這裏請求網絡加載數據

            LogUtils.i(this,"loadInitial "+pageCount);
            //計算顯示到第幾條
            final int position = computeInitialLoadPosition(params, PAGE_SIZE);
            //recyclerView第一次加載時我們調用OkHttp進行數據的加載
            Map<String, String> paramsGet = new HashMap<>();
            paramsGet.put("page", pageCount+"");
            paramsGet.put("count", PAGE_SIZE + "");
            paramsGet.put("type", "video");
            HttpUtils.getAllApiInCompany().getVideoBean(paramsGet).enqueue(new Callback<MusicResponeBean>() {
                @Override
                public void onResponse(Call<MusicResponeBean> call, Response<MusicResponeBean> response) {
                    List<MusicBean> tmpList = response.body().getResult();
                    listData.addAll(tmpList);

                    //step10、
                    //最重要的一步,paging是基於觀察者模式,我們在這裏調用callback.onResult();
                    //會直接將數據list返回到UI層,等下面接受到這個list數據的數據的時候我會提醒大家
                    //如果設置佔位符需要調用三個參數的onResult()方法,最後一個參數爲每頁的總數據量
                    //我們沒有設置佔位符,因此調用兩個參數的方法,注意position通過computeInitialLoadPosition(params, PAGE_SIZE)獲取
                    callback.onResult(listData,position);
                }

                @Override
                public void onFailure(Call<MusicResponeBean> call, Throwable t) {

                }
            });

        }

        /**
         * 當用戶滑動recyclerView到下一屏的時候自動調用,這裏我們自動加載下一頁的數據
         *
         * @param params   包含當前加載的位置position、下一頁加載的長度count
         * @param callback 將數據回調給UI界面使用callback.onResult
         */
        @Override
        public void loadRange(@NonNull LoadRangeParams params, @NonNull final LoadRangeCallback<MusicBean> callback) {

            //step11、請求網絡加載更多數據,執行在子線程
            pageCount++;
            LogUtils.i(this,"loadRange "+pageCount);

            Map<String, String> paramsGet = new HashMap<>();
            paramsGet.put("page", pageCount + "");
            paramsGet.put("count", PAGE_SIZE + "");
            paramsGet.put("type", "video");
            HttpUtils.getAllApiInCompany().getVideoBean(paramsGet)
                    .enqueue(new Callback<MusicResponeBean>() {
                @Override
                public void onResponse(Call<MusicResponeBean> call, Response<MusicResponeBean> response) {
                    List<MusicBean> tmpList = response.body().getResult();
                    listData.addAll(tmpList);

                    //step12、請求網絡加載更多數據成功,調用callback的onResult()函數,傳入總的list觸發ui更新
                    callback.onResult(listData);
                }

                @Override
                public void onFailure(Call<MusicResponeBean> call, Throwable t) {

                }
            });
        }
    }

2、DataSource.Factory,構建PagedList所需

    /**
     * 自定義DataSource.Factory
     * //step7、定義一個DataSource.Facotry的子類,因爲後臺給的接口是那種有pageSize和pageCount的那種,
     * 所以這裏選擇返回的DataSource應該是PositionalDataSource<T>這種,別問我爲什麼,google就是這麼說的。其他的情況可以參考
     * https://www.jianshu.com/p/ad040aab0e66
     */
    class PageDataSourceFacotry extends DataSource.Factory {

        PositionalDataSource<MusicBean> positionalDataSource;

        public PageDataSourceFacotry(PositionalDataSource<MusicBean> positionalDataSource) {
            this.positionalDataSource = positionalDataSource;
        }

        @Override
        public DataSource create() {
            return positionalDataSource;
        }
    }

3、構建LiveData<PagedList<T>>(使用Paging組件時,不用用List<T>在裝載數據了,需要使用PagedList<T>),通過LivePagedListBuilder.builder方法生成,需要傳入的參分別是前面的的DataSource.Factory和PagedList.Config。

 private void initPagedList() {
        //step13、生成自定義的DataSource對象
        PositionalDataSource<MusicBean> positionalDataSource = new MyDataSource();

        //step14、通過PagedList.Config.Builder()生成配置對象,傳入的到LivePagedListBuilder中
        PagedList.Config config = new PagedList.Config.Builder()
                .setPageSize(PAGE_SIZE)//每次加載的數據數量15
                //距離本頁數據幾個時候開始加載下一頁數據(例如現在加載10個數據,設置prefetchDistance爲2,則滑到第八個數據時候開始加載下一頁數據).
                .setPrefetchDistance(5)//15
                //這裏設置是否設置PagedList中的佔位符,如果設置爲true,我們的數據數量必須固定,由於網絡數據數量不固定,所以設置false.
                .setEnablePlaceholders(false)
                .setInitialLoadSizeHint(PAGE_SIZE)//15
                .build();


        /*
        //step6、新建LiveData<PagedList<T>>,需要通過LivePagedListBuilder來完成,裏面需要兩個參數。
        一個是:DataSource.Factor,作用就是返回一個DataSource,有幾種類型,(PositionalDataSource適合接口是帶pageCount,pageSize)
        一個是:PagedList.Config,配置主要分頁加載參數,如每頁加載多少條數據等
        */
        // 構建LiveData
        pagedListLiveData = new LivePagedListBuilder(new PageDataSourceFacotry(positionalDataSource)//自己定義
                , config).build();

    }

 

綁定UI部分

1、原來的adapter需要實現PagedListAdapter,注意獲取itemBean的時候需要用getItem()

2、給LiveData註冊observer的時候,通過adapter.submitList(PagedList)綁定

public class SamplePagingActivity extends AppCompatActivity {

    RecyclerView recyclerView;
    private PagingViewModel pagingViewModel;
    private MyPagingAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample_paging);


        pagingViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()).create(PagingViewModel.class);

        //step3、獲取ViewModel中的LiveData(LiveData<T> ,T必須要是一個PagedList<>類型),註冊觀察者
        pagingViewModel.getPagedListLiveData().observe(this, new Observer<PagedList<MusicBean>>() {
            @Override
            public void onChanged(@Nullable PagedList<MusicBean> musicBeans) {
                //step4、通過submitList()函數觸發adapter數據更新
                adapter.submitList(musicBeans);
            }
        });

        recyclerView = findViewById(R.id.rv);

        //step1、adapter換成PagingAdapter
         adapter = new MyPagingAdapter( new DiffUtil.ItemCallback<MusicBean>() {
            @Override
            public boolean areItemsTheSame(@NonNull MusicBean musicBean, @NonNull MusicBean t1) {
                return musicBean.text.equals(t1.text);//比對唯一標識碼
//                return false;
            }

            @SuppressLint("DiffUtilEquals")
            @Override
            public boolean areContentsTheSame(@NonNull MusicBean musicBean, @NonNull MusicBean t1) {
                return musicBean.equals(t1);//比對對象
//                return false;
            }
        });
        recyclerView.setAdapter(adapter);

    }

    class MyPagingAdapter extends PagedListAdapter<MusicBean, RecyclerView.ViewHolder> {

        protected MyPagingAdapter(@NonNull DiffUtil.ItemCallback<MusicBean> diffCallback) {
            super(diffCallback);
        }

        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {

            View view = LayoutInflater.from(SamplePagingActivity.this).inflate(android.R.layout.simple_list_item_1, null);
            RecyclerView.ViewHolder viewholder = new RecyclerView.ViewHolder(view) {
            };
            return viewholder;
        }

        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
            //step2、獲取list的方法必須換成PagingAdapter的getItem()函數
            String str = getItem(i).text;
            ((TextView) viewHolder.itemView.findViewById(android.R.id.text1)).setText(str);
        }
    }


}

 

這裏先簡述一下流程:

  1. 首先我們需要一個DataSource:根據後臺藉口,應用不同的DataSource,Android api封裝了3種
  2. 然後創建一個DataSource.Factory:實現抽象方法,返回一個DataSource
  3. 創建一個PagedList.Config:設置加載配置,如每頁加載多少條數據
  4. 在ViewModel中創建LiveData<PagedList<T>>,通過LivePagedListBuilder創建,需要用到上面3個數據
  5. adapter換成PagingAdapter,bindView中獲取list的方法必須換成PagingAdapter的getItem()函數
  6. 獲取ViewModel中的LiveData(LiveData<T> ,T必須要是一個PagedList<>類型),註冊觀察者,在onChanged()內通過submitList()函數觸發adapter數據更新

 

 

對於Android Jetpack的套件,我正在整理一個比較簡單,適合初學者的Demo,特別適合快速入門,有興趣的童鞋們可以去下載看看:(待續)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章