How to implement endless list with RecyclerView?
Thanks to @Kushal and this is how I implemented it
private boolean loading = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int 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("...", "Last Item Wow !");
// Do pagination.. i.e. fetch new data
loading = true;
}
}
}
}
});
Don't forget to add
LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
Android Endless List
One solution is to implement an OnScrollListener
and make changes (like adding items, etc.) to the ListAdapter
at a convenient state in its onScroll
method.
The following ListActivity
shows a list of integers, starting with 40, adding items when the user scrolls to the end of the list.
public class Test extends ListActivity implements OnScrollListener {
Aleph0 adapter = new Aleph0();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(adapter);
getListView().setOnScrollListener(this);
}
public void onScroll(AbsListView view,
int firstVisible, int visibleCount, int totalCount) {
boolean loadMore = /* maybe add a padding */
firstVisible + visibleCount >= totalCount;
if(loadMore) {
adapter.count += visibleCount; // or any other amount
adapter.notifyDataSetChanged();
}
}
public void onScrollStateChanged(AbsListView v, int s) { }
class Aleph0 extends BaseAdapter {
int count = 40; /* starting amount */
public int getCount() { return count; }
public Object getItem(int pos) { return pos; }
public long getItemId(int pos) { return pos; }
public View getView(int pos, View v, ViewGroup p) {
TextView view = new TextView(Test.this);
view.setText("entry " + pos);
return view;
}
}
}
You should obviously use separate threads for long running actions (like loading web-data) and might want to indicate progress in the last list item (like the market or gmail apps do).
How to implement endless scrolling listview
you didn't use EndlessListView in your Fragment. As I looked your response, it includes pagination. So, you need to request with page number to use endless scroll.
This is just flow for Endless Listview. If your code still doesn't work, I hope you can learn sample projects after read this messages. I added only parts you need. Thanks
Endless scroll only shows limited data on listview. When user scroll down to bottom, it will get next limited data from server. So you need to add current_page paramater in your request. I don't know exactly your api.
First, you need variable for current_page which initialize with 0
You have to know when to stop. You response has total_page field for that. Save and add validation
Then you need to know when to request next page. For that,you already have codes in onScroll within EndlessListView. That code calculate and tell when user scroll down to bottom and it called
listener.loadData();
to request new data. But you still need to add listener for that. You already have
public void setListener(EndLessListener listener) {
this.listener = listener;
}
You need to create Interface with the name EndlessListener and implements in your fragment. EndlessListener Interface will include loadData() function that request to server. After that you need to add listener for the listview.
endlessListView.setListener(this);
Endless Scrolling RecyclerView is not working when items in list are minimum then screen size
You can do your load more task something like this in recyclerview if list are minimum then screen size
@Override
public void onBindViewHolder(DataObjectHolder holder, int position) {
holder.label.setText(mDataset.get(position).getmText1());
holder.dateTime.setText(mDataset.get(position).getmText2());
if (position == this.getItemCount() - 1 && !loading){
// do your load more task here
}
}
Endless RecyclerView, that repeat data when goes to the end
You are lucky, I did it a couple of days ago.
The trick in my solution was to override the getItemCount()
of the adapter
so that it works with Integer.MAX_VALUE
value.
The getItemCount()
is used by the recyclerview
to determinate how many items there are in the list, and if it returns always MAX_VALUE
, the list is pretty much infinite.
This is my example:
Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mlayout);
RecyclerView myRv = findViewById(R.id.myRv);
ArrayList<MyObject> objectList = new ArrayList<>();
objectList = retrieveObjectList();
myRv.setLayoutManager(new SlowLayoutManager(myActivity.this));
MyAdapter myAdapter = new MyAdapter(objectList);
myRv.setAdapter(myAdapter);
}
Adapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList<MyObject> myObjects;
public MyAdapter(ArrayList<MyObject> myObjects) {
this.myObjects = myObjects;
}
//used to retrieve the effective item position in list
public int getActualItemCount() {
if (myObjects == null) {
myObjects = new ArrayList<>();
}
return myObjects.size();
}
@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
@NonNull
@Override
public MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MyAdapter.MyViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_view, parent, false));
}
@Override
public void onBindViewHolder(@NonNull MessagesAdapter.MessagesViewHolder holder, int position) {
if (myObjects.size() == 0) {
holder.bind(null);
} else {
MyObject myObject = myObjects.get(position % myObjects.size());
holder.bind(SMSMessage);
}
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView myTv;
MessagesViewHolder(View itemView) {
super(itemView);
myTv = itemView.findViewById(R.id.myTv);
}
void bind(MyObject myObject) {
if (myObject != null) {
myTv.setText(myObject.getProperty());
} else {
myTv.setText("");
}
}
}
}
I use this way (obj I changed names so you can fill them with yours, since some of mine were similar to native ones).
If you have any question, ask freely
Implementing Endless RecyclerView
This could achieve your goal.
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();
private int previousTotal = 0; // The total number of items in the dataset after the last load
private boolean loading = true; // True if we are still waiting for the last set of data to load.
private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;
private int current_page = 1;
private LinearLayoutManager mLinearLayoutManager;
public EndlessRecyclerOnScrollListener(LinearLayoutManager linearLayoutManager) {
this.mLinearLayoutManager = linearLayoutManager;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
current_page++;
onLoadMore(current_page);
loading = true;
}
}
public abstract void onLoadMore(int current_page);
}
And sample activity
public class SampleActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setOnScrollListener(new EndlessRecyclerOnScrollListener(linearLayoutManager) {
@Override
public void onLoadMore(int current_page) {
// do something...
}
});
}
}
Edit: See here: Endless Scrolling with AdapterViews
How do I create a circular (endless) RecyclerView?
There is no way of making it infinite, but there is a way to make it look like infinite.
in your adapter override
getCount()
to return something big likeInteger.MAX_VALUE
:@Override
public int getCount() {
return Integer.MAX_VALUE;
}in
getItem()
andgetView()
modulo divide (%) position by real item number:@Override
public Fragment getItem(int position) {
int positionInList = position % fragmentList.size();
return fragmentList.get(positionInList);
}at the end, set current item to something in the middle (or else, it would be endless only in downward direction).
// scroll to middle item
recyclerView.getLayoutManager().scrollToPosition(Integer.MAX_VALUE / 2);
How to implement endless scroll (pagination) in recyclerview with StaggeredGridLayoutManager
For your first problem you already have a solution.
StaggeredGridLayoutManager staggeredGridLayoutManager = new
StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
postRecyclerView.setLayoutManager(
staggeredGridLayoutManager // I have 3 rows
);
For second problem:
postRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener({
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
visibleItemCount = staggeredGridLayoutManager .getChildCount();
totalItemCount = staggeredGridLayoutManager .getItemCount();
int[] firstVisibleItems = null;
firstVisibleItems = mLayoutManager.findFirstVisibleItemPositions(firstVisibleItems);
if(firstVisibleItems != null && firstVisibleItems.length > 0) {
pastVisibleItems = firstVisibleItems[0];
}
if (loading) {
if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
loading = false;
getData()
}
}
}
});
..........
..........
private void getData() {
mStorage = FirebaseStorage.getInstance();
databaseEventListener = databaseReference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
if (snapshot.exists()) {
progressBar.setVisibility(View.GONE);
postRecyclerView.setVisibility(View.VISIBLE);
mUploads.clear();
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
Upload upload = dataSnapshot.getValue(Upload.class);
Objects.requireNonNull(upload).setmKey(dataSnapshot.getKey());
mUploads.add(upload);
}
}
//notify the adapter
postsAdapter.notifyDataSetChanged();
loading = true;
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
loading = true;
}
});
}
You might have to call the getData()
initially in your onCreate()
so that some data loads up on the screen and you have a scroll behavior.
Update:
The second parameter in a StaggeredGridLayoutManager is orientation so instead of context
you have to pass orientation StaggeredGridLayoutManager.VERTICAL
.
Endless Scroll RecyclerView always return to top
set adapter in onCreateView()
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.booking_fragment, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.bookingRecyclerView);
linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
adapter = new BookingAdapter(listBooking,getActivity());
recyclerView.setAdapter(adapter);
scrollListener = new EndlessRecyclerViewScrollListener(linearLayoutManager) {
@Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
// Triggered only when new data needs to be appended to the list
// Add whatever code is needed to append new items to the bottom of the list
if(CurrentStatus.equals("notSearch")){
if (current < Integer.parseInt(TP)) {
current++;
loadMoreBookings(sort);
}
else if(current == Integer.parseInt(TP)){
Toast.makeText(getActivity(),"No More Data to Load", Toast.LENGTH_SHORT).show();
}
}else{
if (current < Integer.parseInt(TP)) {
current++;
searchMoreBookings(search, sort);
}
else if(current == Integer.parseInt(TP)){
Toast.makeText(getActivity(),"No More Data to Be Load", Toast.LENGTH_SHORT).show();
}
}
}
don't set adapter every time just notify the adapter item inserted at position.
private void loadMoreBookings(final String sort){
CurrentStatus = "notSearch";
final ProgressDialog progressDialog = new ProgressDialog(getActivity());
progressDialog.setMessage("Please Wait While Retrieving Data");
progressDialog.setCancelable(false);
progressDialog.show();
StringRequest requesting = new StringRequest(Request.Method.POST, URL,
new Response.Listener<String>() {
@Override
public void onResponse(String JSONString) {
progressDialog.dismiss();
try{
JSONObject jsonTP = new JSONObject(JSONString);
JSONArray jsonArrayB = jsonTP.getJSONArray("Data");
for(int i = 0; i < jsonArrayB.length(); i++){
JSONObject o = jsonArrayB.getJSONObject(i);
Booking list = new Booking(
o.getString("bookID"),
o.getString("userEmail"),
o.getString("paymentMethod"),
o.getString("paymentStatus"),
o.getString("totalPrice"),
o.getString(String.valueOf("securityCode")),
o.getString(String.valueOf("travelDate")),
o.getString("paymentID"),
o.getString("userFN"),
o.getString("userLN"),
o.getString(String.valueOf("createdAt")),
o.getString("tTM"),
o.getString("messageToCustomer"),
o.getString("messageFromMerchant"),
o.getString("wCN"),
o.getString("wLocation"),
o.getString("wWebsite")
);
listBooking.add(list);
adapter.notifyItemInserted(count);
count++;
}
Toast.makeText(getActivity(), "Data : "+count, Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
loadMoreBookings(sort);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
progressDialog.dismiss();
Toast.makeText(getActivity().getApplicationContext(), "Failed To Retrieve Data. Please Try Again.",Toast.LENGTH_LONG).show();
}
}
){
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<String, String>();
String user = getActivity().getIntent().getStringExtra("username");
params.put("username", user);
params.put("currentpage", String.valueOf(current));
params.put("sorting", sort);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(getActivity().getApplicationContext());
requestQueue.add(requesting);
}
Related Topics
Checking If an Android Application Is Running in the Background
Retrieve a Fragment from a Viewpager
How to Filter a Recyclerview With a Searchview
Android Accelerometer Accuracy (Inertial Navigation)
Service VS Intentservice in the Android Platform
How to Get HTML Content from a Webview
How to Get Complete Address from Latitude and Longitude
Install/Uninstall Apks Programmatically (Packagemanager VS Intents)
How to Prevent Custom Views from Losing State Across Screen Orientation Changes
How Exactly Does the Android:Onclick Xml Attribute Differ from Setonclicklistener
How to Prevent a Dialog from Closing When a Button Is Clicked
Android Check Internet Connection
Android How to Adjust Layout in Full Screen Mode When Softkeyboard Is Visible