如何快速寫一款小而美的《上滑無限加載的控件》

【版權申明】非商業目的註明出處可自由轉載
博文地址:
出自:shusheng007

前言

在日常從事Android開發工作時,經常會遇到下拉刷新列表頁面,上拉自動加載列表的需求, GitHub上已經有很多關於這方面的功能極其強大的類庫了,那我們還有必要自己寫一個嗎?

答案是:也許。因爲那些類庫實在是太重了!很多時候我們只使用了其5%的功能,但卻不得不忍受剩下那95%的“無用”功能和複雜性。

UI方面,自己實現某些效果很多時候比直接使用現存類庫更合適,關於這個話題這個不能深槓,世界上沒有最好的東西,只有最適合的東西。

今天我們就簡單介紹如何快速寫一個一個既可下拉刷新,又可上滑無限加載的小巧靈活的UI控件。

InfiniteRecyclerView

一般情況下我們只使用Android原生提供的那些控件稍作組合或修改即可完成絕大部分UI需求,讓我們看一下如何通過簡單的組合Android原生的 SwipeRefreshLayoutRecyclerView來製作一個非常輕量而實用的控件。

下圖只演示了無限加載功能:

在這裏插入圖片描述

準備實現的實用功能

  1. 下滑可以刷新頁面
  2. 上滑可以無限加載

其實日常工作需求最多的也就上面兩條。

實現思想

  • 使用SwipeRefreshLayout套一個RecyclerView,如下所示

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    	android:id="@+id/container"
    	xmlns:android="http://schemas.android.com/apk/res/android"
    	android:layout_width="match_parent"
    	android:layout_height="match_parent">
    	<androidx.recyclerview.widget.RecyclerView
    		android:id="@+id/recycle_view"
    		android:layout_width="match_parent"
    		android:layout_height="match_parent"
    		android:scrollbars="vertical"/>
    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    

    SwipeRefreshLayout 負責下滑刷新,RecyclerView, 負責上滑加載

  • 通過RecyclerViewRecyclerView.OnScrollListener 來監聽是否滑動到了最後一個item,如果滑動到了最後一個item則觸發加載更多操作。

     private class OnScrollToBottomListener extends RecyclerView.OnScrollListener {
    
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            ...
            
            if (manager instanceof LinearLayoutManager) {
                int lastCompletelyVisibleItemPosition = ((LinearLayoutManager) manager).findLastCompletelyVisibleItemPosition();
                if (adapter.getItemCount() > 1 && lastCompletelyVisibleItemPosition >= adapter.getItemCount() - 1) {
                    adapter.loadingMore();
                }
            } else if (manager instanceof StaggeredGridLayoutManager) {
                int[] itemPositions = new int[2];
                ((StaggeredGridLayoutManager) manager).findLastVisibleItemPositions(itemPositions);
                int lastVisibleItemPosition = (itemPositions[1] != 0) ? ++itemPositions[1] : ++itemPositions[0];
                if (lastVisibleItemPosition >= adapter.getItemCount()) {
                    adapter.loadingMore();
                }
            }
        }
       ...
    }
    
    1. 當處於加載過程中時,在最後一行展示一個加載更多的 view

    具體實現我覺得還是的自己看源代碼吧,源代碼就是我們程序員有效交流的最好語言。

    如何使用

    第一步:自定義一個Adapter,其需要繼承RefreshRecycleAdapter<T>, 例如下面這樣

    public class BeautyListAdapter extends RefreshRecycleAdapter<Beauty> {
        public BeautyListAdapter(List<Beauty> dataSource) {
            super(dataSource);
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateCustomViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_beauty,parent,false);
            return new BeautyViewHolder(view);
        }
    
        @Override
        public void onBindCustomViewHolder(RecyclerView.ViewHolder holder, int position) {
            BeautyViewHolder beautyViewHolder= (BeautyViewHolder) holder;
            final Beauty beauty=getList().get(position);
            beautyViewHolder.binding.ivBeautyHead.setImageResource(beauty.getPhotoId());
            beautyViewHolder.binding.tvBeautyTitle.setText(beauty.getName());
            beautyViewHolder.binding.tvBeautyIntroduction.setText(beauty.getIntroduction());
        }
    
        static class BeautyViewHolder extends RecyclerView.ViewHolder{
            private final ItemBeautyBinding binding;
            public BeautyViewHolder(@NonNull View itemView) {
                super(itemView);
                binding= ItemBeautyBinding.bind(itemView);
            }
        }
    }
    

    Note:上面的代碼使用了viewBinding功能,對此不熟悉的同學,推薦閱讀Android開發之秒懂ViewBinding,一代神器ButterKnife的終結者

    第二步: 初始化控件併爲其設置AdapterLayoutManager

      mAdapter = new BeautyListAdapter(mViewModel.dataSource);
      mBinding.pullRefreshList.setLayoutManager(new LinearLayoutManager(context));
      mBinding.pullRefreshList.setAdapter(mAdapter);
      mAdapter.setOnRefreshLoadMoreListener(new AdapterLoader.OnRefreshLoadMoreListener() {
          @Override
          public void onRefresh() {
              //此處調用刷新邏輯
          }
    
          @Override
          public void onLoadMore() {
              //此處調用加載更多邏輯
          }
      });
    

    第三步:處理結果

    • 當刷新完成後使用如下代碼告知控件刷新完成,去掉刷新的標誌
    mBinding.pullRefreshList.setRefresh(false);
    
    • 當加載一頁完成後,向數據源增加數據並刷新視圖
    mAdapter.notifyDataSetChanged();
    

    當返回的列表爲空時,說明已經沒有數據了,此時使用如下方法通知控件已經到底了

    mAdapter.setHasMore(false);
    

優勢

  1. 輕量級
  2. 強可擴展性
    你完全可以獲取到SwipeRefreshLayoutRecyclerView,然後對他們進行各種騷操作。
  3. 使用簡單,與RecyclerView的使用方法保持一致,只要會用RecyclerView就可輕鬆使用(如果你說你只會用ListView,不會用RecyclerView,我只能對你漏出鄙夷的微笑了)。

總結

2020年突入其來的新冠病毒疫情已經肆虐了4個月了,很多受疫情影響的公司都快撐不住了,身邊開始出現很多被裁員的同事和朋友,前兩天親眼目睹了一位被裁的同事情緒崩潰的樣子,這件事也給自己敲響了警鐘,單純出賣自己的時間真的是一件很危險的事情,還是要不斷的使自己增值,同時去找一件自己喜歡的事情,並堅持做下去。

耳邊又想起了那個什麼夫斯基的《鋼鐵是怎樣煉成的》:

當他回首往事的時候,他不會因虛度年華而悔恨,也不會因碌碌無爲而羞恥!

當我們回首往事的時候,往往會遺憾沒有做過的事情,而不會悔恨做了什麼事情。

源代碼下載地址:InfiniteRecyclerview

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