How to implement Infinite Scrolling with RecyclerView?
Asked Answered
U

5

3

I have a recycler and inside of it there are cardviews where I fetch information from a REST service, I'm trying to implement an endless scroll, It's supposed that user will see 10 cardviews every time he scrolls down until there are no more cardviews to show, How can I achieve that?

I've seen a few examples but none of them really helped me about how to do it. I don't even know what I need to put in adapter.class or in my Fragment.class because I don't understand how to implement that, it would be great if someone could tell me the correct way to implement the infinite scroll in my code...

Thanks in advance.

MainAdapter.class

public class MainAdapter extends RecyclerView.Adapter<MainAdapter.ViewHolder> implements View.OnClickListener
{
    private ArrayList<Business> businessList;
    private Activity activity;
    private int layoutMolde,idb;

    public MainAdapter(Activity activity, ArrayList<Business> list, int layout) 
    {
        this.activity = activity;
        this.businessList = list;
        layoutMolde = layout;
    }

    @Override
    public MainAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_row, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) 
    {
        holder.mTitle.setText(businessList.get(position).getBusiness_name());
        holder.number_rating.setText(businessList.get(position).getRating().toString());
        Glide.with(activity).load(businessList.get(position).getLogo_url_string()).into(holder.mImg);
    }

    @Override
    public int getItemCount() {
        return businessList.size();
    }

    @Override
    public void onClick(View v) 
    {

    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        public TextView mTitle;
        public ImageView mImg;
        public ImageView logo;
        public RatingBar main_rating;
        public TextView number_rating;

        public ViewHolder( View itemView) 
        {
            super(itemView);
            mTitle = (TextView) itemView.findViewById(R.id.nom_business_main);
            number_rating = (TextView) itemView.findViewById(R.id.number_rating);
            mImg = (ImageView) itemView.findViewById(R.id.img_main);
            main_rating=(RatingBar) itemView.findViewById(R.id.rating_main);
            main_rating.setRating((float)1);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v)
                {

                    Intent in = new Intent(v.getContext(), BusinessPremium.class);
                    int position = getAdapterPosition();
                    idb = businessList.get(position).getId();
                    in.putExtra("no", idb);
                    v.getContext().startActivity(in);
                }
            });
        }
    }
}

FeedsFragment.class

    public class FeedsFragment extends Fragment
    {

        private ArrayList<Business> arrayBusiness,arrayBasics;
        private Gson gson;

        private static final Type BUSINESS_TYPE = new TypeToken<ArrayList<Business>>() {}.getType();
        private RecyclerView.LayoutManager mLayoutManager;

        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        {
            View android = inflater.inflate(R.layout.fragment_feeds, container, false);

            if (!internetConnectionCheck(FeedsFragment.this.getActivity())) 
            {
                Toast.makeText(getApplicationContext(), "Error de Conexión", Toast.LENGTH_LONG).show();
            }

            new RequestBase(getActivity()) {

                @Override
                public JsonObject onHttpOk(JsonObject response) throws JSONException {

                    JsonObject objeto, pagination_details = null, details, premium_img;
                    JsonArray data;

                    if (getActivity() == null)
                        return response;

                    if (response.get("pagination") == null) 
                    {
                        objeto = response;

                    } else {
                        objeto = response;
                        pagination_details = response.get("pagination").getAsJsonObject();
                        data = objeto.get("data").getAsJsonArray();
                        gson = new Gson();
                        arrayBusiness = gson.fromJson(data, BUSINESS_TYPE);
                        Log.d("size", String.valueOf(arrayBusiness.size()));
                        FeedsFragment.this.getActivity().runOnUiThread(new Runnable()
                        {
                            @Override
                            public void run() 
                            {

                                RecyclerView recycler = (RecyclerView) FeedsFragment.this.getActivity().findViewById(R.id.recycler_main);
                                MainAdapter adapter = new MainAdapter(getActivity(), arrayBusiness, R.layout.main_row);

                                recycler.setNestedScrollingEnabled(false);
                                mLayoutManager = new GridLayoutManager(FeedsFragment.this.getActivity(), 2);
                                recycler.setLayoutManager(mLayoutManager);
                                recycler.setAdapter(adapter);

                                GifTextView loading = (GifTextView)FeedsFragment.this.getActivity().findViewById(R.id.loading);
                                TextView loadingText = (TextView)FeedsFragment.this.getActivity().findViewById(R.id.loadingText);
                                loading.setVisibility(View.GONE);
                                loadingText.setVisibility(View.GONE);

                            }
                        });
                    }
                    if (pagination_details.isJsonNull()) {
                        Log.d("Paginacion", pagination_details.toString());
                    }
                    return objeto;
                }

                @Override
                public void onHttpCreate(JsonObject response) throws JSONException 
                {

                }

                @Override
                public void onHttpUnprocessableEntity(JsonObject response) throws JSONException 
                {
                    this.cancel(true);
                    final String error = response.get("errors").toString();
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getActivity().getApplicationContext(), error, Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }.execute("businesses/premiums", "GET");

            return android;
        }
}
Uniseptate answered 24/3, 2017 at 3:57 Comment(1)
use nestedscrollview instead of scrollviewRecliner
M
2

you can refresh using SwipeRefreshLayout in android to refresh and in the on refresh override method call your api

note:put your API call request in a method and call that method inyour onRefresh method of SwipeRefreshLayout

Minhminho answered 24/3, 2017 at 5:28 Comment(0)
L
2

When writing RecyclerView.Adapter, you anyway need to provide the getItemCount method that returns the correct number of items (may be large). RecyclerView will call on its own initiative the onBindViewHolder(holder, position) method of this adapter. All you need is to provide functionality of retrieving data, relevant to this position. There is no difference at all, if your list is smaller than screen, slightly larger than screen or Integer.MAX_VALUE size. RecyclerView will take care not to fetch/allocate too much extra items.

You do not need to implement scroll listeners or otherwise explicitly handle the scrolling.

The only tricky part is that you may need to take a long action like server call to get some items. Then just return uninitialized holder (empty view) on the first invocation and start fetching the needed row in the background thread. When you have it, call notifyDataSetChanged or notifyItemRangeChanged, and RecyclerView will take care to update itself.

For performance reasons I would strongly recommend to update content in chunks of the fixed size rather than sending individual server request per every row displayed. For some public servers like Google Books this is clearly a requirement, as they have quota limits per request.

If you need to view the full source code on how this possibly could be implemented, there is an open source project here in GitHub.

Livelong answered 29/5, 2017 at 20:3 Comment(0)
B
1

Make a static boolean variable named "ready" and initialize it to false.

Add the if ready condition in the onLoadMore method as below.

public boolean onLoadMore(int page, int totalItemsCount) {
         if (ready) {
           //load more from API
         }         
   return false;
}

set ready to true in onBindViewHolder when the position of item is last.

Bellicose answered 25/3, 2017 at 20:56 Comment(0)
H
1

Here is a way that a colleague of mine introduced. we worked in it together and i implemented it successfully with no issues. I wanted to give back to anyone having this issue.

in your adapter you need to set the count to be infinite size and then when you want the position of an item you should use val loopPos = position % dataSource.size anytime you need the position. lets take a look how this can be done in a recyclerView adapter but could also be applied to FragmentStatePagerAdapter.

class InfiniteLoopingHorizontalRecyclerViewAdapter(var dataSource: ArrayList<String>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {


override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    val inflatedView: View = LayoutInflater.from(parent.context)
            .inflate(R.layout.your_finite_layout, parent, false)
    return ItemHolder(inflatedView)
}

override fun getItemCount(): Int {
    return Integer.MAX_VALUE  //***** this should be high enough - wink wink ******
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
//****** this is critical here when you need the position use the loopPos ****/
    val loopPos = position % dataSource.size
    (holder as? ItemHolder)?.bind(dataSource[loopPos], loopPos)
}

inner class ItemHolder(view: View) : RecyclerView.ViewHolder(view) {

    fun bind(myString: String, position: Int) = with(itemView) {
      myTextView.setText(myString)
    }
}

}

how it works:

lets say your dataSource size is 50 but your position is at 51 that means the following: 51%50 . which gives you position 1. and lets say again your position is 57 and again your dataSource size is still 50. that means your position is 7. so to be clear, anytime you need a infinite affect you can use the modules of the position and the dataSource size.

ps: lets go crazy and say we scrolled to position 11323232323214 then that means 11323232323214%50 = 14 so its position 14 in your datasource that will be used. you can then polish off the affect with wrapping your recyclerview in a SnapHelper class

Hanson answered 29/8, 2018 at 11:17 Comment(1)
This shows the same data over and over again? I don't think that's what they were looking for.Amends
S
0

You can add a scrollListener to your recyclerview.

Check a similar answer here

And the main SO post here

Where, the scrollListener will check where exactly are you in the recyclerview and based on some logic (which you can flexibly write) make a second call!

Scaly answered 24/3, 2017 at 6:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.