How to implement endless list with RecyclerView?

前端 未结 30 2693
无人及你
无人及你 2020-11-22 02:22

I would like to change ListView to RecyclerView. I want to use the onScroll of the OnScrollListener in RecyclerView to determine if a

相关标签:
30条回答
  • 2020-11-22 03:12

    My way to detect loading event is not to detect scrolling, but to listen whether the last view was attached. If the last view was attached, I regard it as timing to load more content.

    class MyListener implements RecyclerView.OnChildAttachStateChangeListener {
        RecyclerView mRecyclerView;
    
        MyListener(RecyclerView view) {
            mRecyclerView = view;
        }
    
        @Override
        public void onChildViewAttachedToWindow(View view) {
    
        RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
        RecyclerView.LayoutManager mgr = mRecyclerView.getLayoutManager();
        int adapterPosition = mgr.getPosition(view);
    
        if (adapterPosition == adapter.getItemCount() - 1) {
            // last view was attached
            loadMoreContent();
        }
    
        @Override
        public void onChildViewDetachedFromWindow(View view) {}
    }
    
    0 讨论(0)
  • 2020-11-22 03:12

    Here my solution using AsyncListUtil, in the web says: Note that this class uses a single thread to load the data, so it suitable to load data from secondary storage such as disk, but not from network. but i am using odata to read the data and work fine. I miss in my example data entities and network methods. I include only the example adapter.

    public class AsyncPlatoAdapter extends RecyclerView.Adapter {
    
        private final AsyncPlatoListUtil mAsyncListUtil;
        private final MainActivity mActivity;
        private final RecyclerView mRecyclerView;
        private final String mFilter;
        private final String mOrderby;
        private final String mExpand;
    
        public AsyncPlatoAdapter(String filter, String orderby, String expand, RecyclerView recyclerView, MainActivity activity) {
            mFilter = filter;
            mOrderby = orderby;
            mExpand = expand;
            mRecyclerView = recyclerView;
            mActivity = activity;
            mAsyncListUtil = new AsyncPlatoListUtil();
    
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View itemView = LayoutInflater.from(parent.getContext()).
                    inflate(R.layout.plato_cardview, parent, false);
    
            // Create a ViewHolder to find and hold these view references, and
            // register OnClick with the view holder:
            return new PlatoViewHolderAsync(itemView, this);
        }
    
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            final Plato item = mAsyncListUtil.getItem(position);
            PlatoViewHolderAsync vh = (PlatoViewHolderAsync) holder;
            if (item != null) {
                Integer imagen_id = item.Imagen_Id.get();
                vh.getBinding().setVariable(BR.plato, item);
                vh.getBinding().executePendingBindings();
                vh.getImage().setVisibility(View.VISIBLE);
                vh.getProgress().setVisibility(View.GONE);
                String cacheName = null;
                String urlString = null;
                if (imagen_id != null) {
                    cacheName = String.format("imagenes/imagen/%d", imagen_id);
                    urlString = String.format("%s/menusapi/%s", MainActivity.ROOTPATH, cacheName);
                }
                ImageHelper.downloadBitmap(mActivity, vh.getImage(), vh.getProgress(), urlString, cacheName, position);
            } else {
                vh.getBinding().setVariable(BR.plato, item);
                vh.getBinding().executePendingBindings();
                //show progress while loading.
                vh.getImage().setVisibility(View.GONE);
                vh.getProgress().setVisibility(View.VISIBLE);
            }
        }
    
        @Override
        public int getItemCount() {
            return mAsyncListUtil.getItemCount();
        }
    
        public class AsyncPlatoListUtil extends AsyncListUtil<Plato> {
            /**
             * Creates an AsyncListUtil.
             */
            public AsyncPlatoListUtil() {
                super(Plato.class, //my data class
                        10, //page size
                        new DataCallback<Plato>() {
                            @Override
                            public int refreshData() {
                                //get count calling ../$count ... odata endpoint
                                return countPlatos(mFilter, mOrderby, mExpand, mActivity);
                            }
    
                            @Override
                            public void fillData(Plato[] data, int startPosition, int itemCount) {
                                //get items from odata endpoint using $skip and $top
                                Platos p = loadPlatos(mFilter, mOrderby, mExpand, startPosition, itemCount, mActivity);
                                for (int i = 0; i < Math.min(itemCount, p.value.size()); i++) {
                                    data[i] = p.value.get(i);
                                }
    
                            }
                        }, new ViewCallback() {
                            @Override
                            public void getItemRangeInto(int[] outRange) {
                                //i use LinearLayoutManager in the RecyclerView
                                LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
                                outRange[0] = layoutManager.findFirstVisibleItemPosition();
                                outRange[1] = layoutManager.findLastVisibleItemPosition();
                            }
    
                            @Override
                            public void onDataRefresh() {
                                mRecyclerView.getAdapter().notifyDataSetChanged();
                            }
    
                            @Override
                            public void onItemLoaded(int position) {
                                mRecyclerView.getAdapter().notifyItemChanged(position);
                            }
                        });
                mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                        onRangeChanged();
                    }
                });
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 03:14

    Make these variables.

    private int previousTotal = 0;
    private boolean loading = true;
    private int visibleThreshold = 5;
    int firstVisibleItem, visibleItemCount, totalItemCount;
    

    Set on Scroll for recycler view.

    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
    
            visibleItemCount = mRecyclerView.getChildCount();
            totalItemCount = mLayoutManager.getItemCount();
            firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
    
            if (loading) {
                if (totalItemCount > previousTotal) {
                    loading = false;
                    previousTotal = totalItemCount;
                }
            }
            if (!loading && (totalItemCount - visibleItemCount) 
                <= (firstVisibleItem + visibleThreshold)) {
                // End has been reached
    
                Log.i("Yaeye!", "end called");
    
                // Do something
    
                loading = true;
            }
        }
    });
    

    Note : Make sure you are using LinearLayoutManager as layout manager for RecyclerView.

    LinearLayoutManager mLayoutManager;
    mLayoutManager = new LinearLayoutManager(this);
    mRecyclerView.setLayoutManager(mLayoutManager);
    

    and for a grid

    GridLayoutManager mLayoutManager;
    mLayoutManager = new GridLayoutManager(getActivity(), spanCount);
    mRecyclerView.setLayoutManager(mLayoutManager);
    

    Have fun with your endless scrolls !! ^.^

    Update : mRecyclerView.setOnScrollListener() is deprecated just replace with mRecyclerView.addOnScrollListener() and the warning will be gone! You can read more from this SO question.

    Since Android now officially support Kotlin, here is an update for the same -

    Make OnScrollListener

    class OnScrollListener(val layoutManager: LinearLayoutManager, val adapter: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>, val dataList: MutableList<Int>) : RecyclerView.OnScrollListener() {
        var previousTotal = 0
        var loading = true
        val visibleThreshold = 10
        var firstVisibleItem = 0
        var visibleItemCount = 0
        var totalItemCount = 0
    
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
    
            visibleItemCount = recyclerView.childCount
            totalItemCount = layoutManager.itemCount
            firstVisibleItem = layoutManager.findFirstVisibleItemPosition()
    
            if (loading) {
                if (totalItemCount > previousTotal) {
                    loading = false
                    previousTotal = totalItemCount
                }
            }
    
            if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
                val initialSize = dataList.size
                updateDataList(dataList)
                val updatedSize = dataList.size
                recyclerView.post { adapter.notifyItemRangeInserted(initialSize, updatedSize) }
                loading = true
            }
        }
    }
    

    and add it to your RecyclerView like this

    recyclerView.addOnScrollListener(OnScrollListener(layoutManager, adapter, dataList))
    

    For a full code example, feel free to refer this Github repo.

    0 讨论(0)
  • 2020-11-22 03:14

    Here is another approach. It will work with any layout manager.

    1. Make Adapter class abstract
    2. Then create an abstract method in adapter class (eg. load())
    3. In onBindViewHolder check the position if last and call load()
    4. Override the load() function while creating the adapter object in your activity or fragment.
    5. In the overided load function implement your loadmore call

    For a detail understanding I wrote a blog post and example project get it here http://sab99r.com/blog/recyclerview-endless-load-more/

    MyAdapter.java

    public abstract class MyAdapter extends RecyclerView.Adapter<ViewHolder>{
    
            @Override
            public void onBindViewHolder(ViewHolder holder, int position) {
                //check for last item
                if ((position >= getItemCount() - 1))
                    load();
            }
    
            public abstract void load();
    }
    

    MyActivity.java

    public class MainActivity extends AppCompatActivity {
        List<Items> items;
        MyAdapter adapter;
    
       @Override
        protected void onCreate(Bundle savedInstanceState) {
        ...
        adapter=new MyAdapter(items){
                @Override
                public void load() {
                    //implement your load more here
                    Item lastItem=items.get(items.size()-1);
                    loadMore();
                }
            };
       }
    }
    
    0 讨论(0)
  • 2020-11-22 03:14
     recyclerList.setOnScrollListener(new RecyclerView.OnScrollListener() 
                {
                    @Override
                    public void onScrolled(RecyclerView recyclerView, int dx,int dy)
                    {
                        super.onScrolled(recyclerView, dx, dy); 
                    }
    
                    @Override
                    public void onScrollStateChanged(RecyclerView recyclerView,int newState) 
                    {
                        int totalItemCount = layoutManager.getItemCount();
                        int lastVisibleItem = layoutManager.findLastVisibleItemPosition();
    
                        if (totalItemCount> 1) 
                        {
                            if (lastVisibleItem >= totalItemCount - 1) 
                            {
                                // End has been reached
                                // do something 
                            }
                        }          
                    }
                });  
    
    0 讨论(0)
  • 2020-11-22 03:15

    I have created LoadMoreRecyclerView using Abdulaziz Noor Answer

    LoadMoreRecyclerView

    public class LoadMoreRecyclerView extends RecyclerView  {
    
        private boolean loading = true;
        int pastVisiblesItems, visibleItemCount, totalItemCount;
        //WrapperLinearLayout is for handling crash in RecyclerView
        private WrapperLinearLayout mLayoutManager;
        private Context mContext;
        private OnLoadMoreListener onLoadMoreListener;
    
        public LoadMoreRecyclerView(Context context) {
            super(context);
            mContext = context;
            init();
        }
    
        public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            mContext = context;
            init();
        }
    
        public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            mContext = context;
            init();
        }
    
        private void init(){
            mLayoutManager = new WrapperLinearLayout(mContext,LinearLayoutManager.VERTICAL,false);
            this.setLayoutManager(mLayoutManager);
            this.setItemAnimator(new DefaultItemAnimator());
            this.setHasFixedSize(true);
        }
    
        @Override
        public void onScrolled(int dx, int dy) {
            super.onScrolled(dx, dy);
    
            if(dy > 0) //check for scroll down
            {
                visibleItemCount = mLayoutManager.getChildCount();
                totalItemCount = mLayoutManager.getItemCount();
                pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
    
                if (loading)
                {
                    if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount)
                    {
                        loading = false;
                        Log.v("...", "Call Load More !");
                        if(onLoadMoreListener != null){
                            onLoadMoreListener.onLoadMore();
                        }
                        //Do pagination.. i.e. fetch new data
                    }
                }
            }
        }
    
        @Override
        public void onScrollStateChanged(int state) {
            super.onScrollStateChanged(state);
        }
    
        public void onLoadMoreCompleted(){
            loading = true;
        }
    
        public void setMoreLoading(boolean moreLoading){
            loading = moreLoading;
        }
    
        public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
            this.onLoadMoreListener = onLoadMoreListener;
        }
    }
    

    WrapperLinearLayout

    public class WrapperLinearLayout extends LinearLayoutManager
    {
        public WrapperLinearLayout(Context context, int orientation, boolean reverseLayout) {
            super(context, orientation, reverseLayout);
        }
    
        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            try {
                super.onLayoutChildren(recycler, state);
            } catch (IndexOutOfBoundsException e) {
                Log.e("probe", "meet a IOOBE in RecyclerView");
            }
        }
    }
    

    //Add it in xml like

    <your.package.LoadMoreRecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </your.package.LoadMoreRecyclerView>
    

    OnCreate or onViewCreated

    mLoadMoreRecyclerView = (LoadMoreRecyclerView) view.findViewById(R.id.recycler_view);
    mLoadMoreRecyclerView.setOnLoadMoreListener(new OnLoadMoreListener() {
                @Override
                public void onLoadMore() {
                    callYourService(StartIndex);
                }
            });
    

    callYourService

    private void callYourService(){
        //callyour Service and get response in any List
    
        List<AnyModelClass> newDataFromServer = getDataFromServerService();
        //Enable Load More
        mLoadMoreRecyclerView.onLoadMoreCompleted();
    
        if(newDataFromServer != null && newDataFromServer.size() > 0){
                StartIndex += newDataFromServer.size();
    
                if (newDataFromServer.size() < Integer.valueOf(MAX_ROWS)) {
                        //StopLoading..
                       mLoadMoreRecyclerView.setMoreLoading(false);
                }
        }
        else{
                mLoadMoreRecyclerView.setMoreLoading(false);
                mAdapter.notifyDataSetChanged();
        }
    }
    
    0 讨论(0)
提交回复
热议问题