I have been very meticulous in following this guide to implement pagination in my RecyclerView. Only difference is I'm using Retrofit to retrieve data from the API.
When I open my activity it stops after a while, and when I check Logcat, it says: IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{31a22aa position=4 id=-1, oldPos=2,... This is what my RecyclerView adapter looks like:
public class CuratedSectionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static int VIEW_TYPE_HEADER = 0;
private static int VIEW_TYPE_ITEM = 1;
private static int VIEW_TYPE_LOADING = 2;
private RecyclerView mRecyclerView;
private OnLoadMoreListener mOnLoadMoreListener;
private boolean isLoading;
private int visibleThreshold = 2;
private int lastVisibleItem, totalItemCount;
private List<Object> itemList;
public CuratedSectionAdapter(List<Object> itemList, RecyclerView recyclerView, LinearLayoutManager layoutManager) {
this.itemList = itemList;
this.mRecyclerView = recyclerView;
final LinearLayoutManager linearLayoutManager = layoutManager;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
isLoading = true;
}
}
});
}
public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {
this.mOnLoadMoreListener = mOnLoadMoreListener;
}
private class ItemViewHolder extends RecyclerView.ViewHolder {
RecyclerView itemRecyclerView;
CuratedSectionNestedAdapter nestedAdapter;
LinearLayoutManager layoutManager;
ItemViewHolder(View view) {
super(view);
itemRecyclerView = view.findViewById(R.id.recyclerView_nested);
layoutManager = new LinearLayoutManager(view.getContext(), LinearLayoutManager.HORIZONTAL, false);
}
}
private class HeaderViewHolder extends RecyclerView.ViewHolder {
TextView textViewHeader;
Typeface montserratMedium = Typeface.createFromAsset(getApplicationContext().getAssets(), "fonts/Montserrat-Medium.ttf");
HeaderViewHolder(View view) {
super(view);
textViewHeader = view.findViewById(R.id.textView_header);
textViewHeader.setTypeface(montserratMedium);
}
}
private class LoadingViewHolder extends RecyclerView.ViewHolder {
ProgressBar progressBar;
LoadingViewHolder(View itemView) {
super(itemView);
progressBar = itemView.findViewById(R.id.progress_bar);
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == VIEW_TYPE_HEADER) {
HeaderViewHolder viewHolder = (HeaderViewHolder) holder;
CuratedSectionHeader header = (CuratedSectionHeader) itemList.get(position);
viewHolder.textViewHeader.setText(header.getHeaderName());
} else if (holder.getItemViewType() == VIEW_TYPE_ITEM){
ItemViewHolder viewHolder = (ItemViewHolder) holder;
List<CuratedSectionItem> items = (List<CuratedSectionItem>) itemList.get(position);
if (viewHolder.nestedAdapter != null) {
viewHolder.nestedAdapter.setItems(items);
} else {
viewHolder.nestedAdapter = new CuratedSectionNestedAdapter(items);
viewHolder.itemRecyclerView.setLayoutManager(viewHolder.layoutManager);
viewHolder.itemRecyclerView.setAdapter(viewHolder.nestedAdapter);
}
} else if (holder.getItemViewType() == VIEW_TYPE_LOADING){
LoadingViewHolder viewHolder = (LoadingViewHolder) holder;
viewHolder.progressBar.setIndeterminate(true);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_HEADER) {
return new HeaderViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.main_explore_header_row, parent, false));
} else if (viewType == VIEW_TYPE_ITEM) {
return new ItemViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.main_explore_row, parent, false));
} else if (viewType == VIEW_TYPE_LOADING) {
return new LoadingViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_loading_item, parent, false));
}
throw new RuntimeException("Adapter " + viewType + "not found");
}
#Override
public int getItemCount() {
return itemList.size();
}
#Override
public int getItemViewType(int position) {
if(isHeader(position)) {
return VIEW_TYPE_HEADER;
} else if(isLoading(position)) {
return VIEW_TYPE_LOADING;
} else {
return VIEW_TYPE_ITEM;
}
}
public boolean isHeader(int position) {
return itemList.get(position) instanceof CuratedSectionHeader;
}
public boolean isLoading(int position) {
return position > itemList.size();
}
public void setLoaded() {
isLoading = false;
}
}
It may have something to do with how I constructed LoadingViewHolder, but I think I did that one correctly. On the other hand, this is what the activity looks like:
public class ExploreFeedActivity extends Activity {
private RecyclerView recyclerView;
private CuratedSectionAdapter curatedSectionAdapter;
private LinearLayoutManager layoutManager;
private Sections curated;
int mStart = 0;
int mLimit = 2;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_explore);
curated = new Sections();
curated.loadSections(mStart, mLimit);
recyclerView = findViewById(R.id.recycler_view_main);
layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
}
private class Sections {
ArrayList<CuratedSection> sections = new ArrayList<>();
public Thread[] thread;
private Sections() {}
public void setSections(ArrayList<CuratedSection> sections) {
this.sections = sections;
}
public void setSectionStories(String sectionId, List<CuratedSectionItem> stories) {
for(CuratedSection s : sections){
if(s.id != null && s.id.equals(sectionId)) {
s.stories = stories;
}
}
}
public void loadStories(String sessionKey) {
thread = new Thread[sections.size()];
for( int s = 0; s < sections.size(); s++) {
thread[s] = new Thread(new LoadStories(sessionKey, sections.get(s)));
thread[s].start();
}
for( int f = 0; f < sections.size(); f++) {
try {
thread[f].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
curatedSectionAdapter = new CuratedSectionAdapter(this.getAdapterInfo(), recyclerView, layoutManager);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(curatedSectionAdapter);
curatedSectionAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
Log.e("haint", "Load More");
curated.sections.add(null);
curatedSectionAdapter.notifyItemInserted(curated.sections.size() - 1);
// Load more data for recyclerview
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Log.e("haint", "Load Further");
// Remove loading item
curated.sections.remove(curated.sections.size() - 1);
curatedSectionAdapter.notifyItemRemoved(curated.sections.size());
// Load data
curated.loadSections(2, 2);
curatedSectionAdapter.notifyDataSetChanged();
curatedSectionAdapter.setLoaded();
}
}, 5000);
}
});
}
public void loadSections(int start, int limit) {
swipeRefreshLayout.setRefreshing(false);
LoadSections load = new LoadSections(start, limit);
load.run();
}
public List<Object> getAdapterInfo() {
List<Object> list = new ArrayList<>();
for (int i = 0; i < sections.size(); i++) {
CuratedSection section = sections.get(i);
CuratedSectionHeader header = new CuratedSectionHeader();
header.setHeaderName(section.header);
list.add(header);
list.add(section.stories);
}
return list;
}
}
private class LoadSections implements Runnable {
int start, limit;
LoadSections(int start, int limit) {
this.start = start;
this.limit = limit;
}
#Override
public void run() {
SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
final String sessionKey = prefs.getString("session_key", null);
Call<JsonArray> call;
call = TravelersApi.endpoint().getCuratedSections(sessionKey);
call.enqueue(new Callback<JsonArray>() {
#Override
public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
if(response.code() != 200) {
Toast.makeText(getApplicationContext(), "Cannot load page as of the moment.", Toast.LENGTH_SHORT).show();
return;
}
JsonArray rawSections = response.body();
if(rawSections.size() == 0) {
//TODO: show placeholder
return;
}
for(int i = start; i < start+limit; i++) {
JsonObject jSection = rawSections.get(i).getAsJsonObject();
final CuratedSection section = new CuratedSection();
section.id = jSection.get("id").getAsString();
section.header = jSection.get("section_header").getAsString();
section.isShown = jSection.get("is_shown").getAsBoolean();
section.stories = new ArrayList<>();
curated.sections.add(section);
}
curated.setSections(curated.sections);
curated.loadStories(sessionKey);
}
#Override
public void onFailure(Call<JsonArray> call, Throwable t) {
Log.d("ERROR!", t.toString());
t.printStackTrace();
}
});
}
}
private class LoadStories implements Runnable {
String sessionKey;
CuratedSection section;
LoadStories(String sessionKey, CuratedSection section) {
this.sessionKey = sessionKey;
this.section = section;
}
#Override
public void run() {
Call<JsonArray> subCall;
subCall = TravelersApi.endpoint().getCuratedSectionTopics(sessionKey, section.id);
try {
Response<JsonArray> response = subCall.execute();
if(response.code() != 200) {
Toast.makeText(getApplicationContext(), "Cannot load page as of the moment.", Toast.LENGTH_SHORT).show();
return;
}
JsonArray rawStories = response.body();
if(rawStories.size() == 0) {
//TODO: show placeholder
return;
}
ArrayList<CuratedSectionItem> stories = new ArrayList<>();
for(int i = 0; i < rawStories.size(); i++) {
JsonObject jStories = rawStories.get(i).getAsJsonObject();
JSONObject temp = new JSONObject(jStories.toString());
JsonObject author = jStories.get("author").getAsJsonObject();
CuratedSectionItem story = new CuratedSectionItem();
story.title = jStories.get("title").getAsString();
story.avatar = author.get("profile_photo").getAsString();
stories.add(story);
}
section.stories = stories;
} catch (IOException e) {
Log.d("ERROR!", e.toString());
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
It's interesting to note that my RecyclerView adapter curatedSectionAdapter was instantiated in the method loadStories inside the private class Sections. I have tried moving that part to onCreate but then the activity won't display anything. I also suspect that the way I constructed the for loop in the callback in LoadSections has something to do with the crash.
Related
I am currently working on an app, that finds all MP3s on a users phone and then puts them into a list. This works very fine and is very quick, even with many songs. Now I populate a new list with an object for each item of the list to then display it inside my recyclerview. The problem is, that I have 700+ songs on my phone and this blocks the UI thread quite some time.
Now, I want to use the recyclerview to not load all items from the list into the objects all at once but rather only when they are about to be displayed - but I have NO clue over how to do this. Right now, all objects are build and then displayed in a very long scrollview from the recyclerview after the UI thread has been blocked for a good 30 seconds. Can please anyone help me? Here is my code:
namespace Media_Player
{
[Activity(Label = "Media_Player", MainLauncher = true)]
public class MainActivity : Activity
{
static public MediaPlayer mediaPlayer;
List<MP3object> mp3;
MediaMetadataRetriever reader;
public static Button btn_StartOrPause, btn_Stop;
public static TextView txt_CurrentSong;
public static bool stopIsActive = false, firstStart = true;
public static Android.Net.Uri CurrentActiveSongUri;
RecyclerView mRecyclerView;
RecyclerView.LayoutManager mLayoutManager;
PhotoAlbumAdapter mAdapter;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.test);
reader = new MediaMetadataRetriever();
PopulateMP3List(ReturnPlayableMp3(true));
mediaPlayer = new MediaPlayer();
InitRecView();
}
private void InitRecView()
{
// Instantiate the adapter and pass in its data source:
mAdapter = new PhotoAlbumAdapter(mp3);
// Get our RecyclerView layout:
mRecyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
// Plug the adapter into the RecyclerView:
mRecyclerView.SetAdapter(mAdapter);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.SetLayoutManager(mLayoutManager);
}
private void PopulateMP3List(List<string> content)
{
mp3 = new List<MP3object>();
foreach (string obj in content)
{
WriteMetaDataToFileList(obj);
}
}
void WriteMetaDataToFileList(string obj)
{
reader.SetDataSource(obj);
//Write Mp3 as object to global list
MP3object ob = new MP3object();
{
if(reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle) != null)
{
ob.SongName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle);
}
else
{
ob.SongName = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist) != null)
{
ob.ArtistName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist);
}
else
{
ob.ArtistName = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyAlbum) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyAlbum) != null)
{
ob.AlbumName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyAlbum);
}
else
{
ob.AlbumName = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != null)
{
ob.Year = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear);
}
else
{
ob.Year = Resources.GetString(Resource.String.Unknown);
}
if (reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != "" && reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear) != null)
{
ob.Year = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyYear);
}
else
{
ob.Year = Resources.GetString(Resource.String.Unknown);
}
ob.Mp3Uri = obj; // can never be unknown!
ob.DurationInSec = int.Parse(reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyDuration)) / 1000; // can never be unknown, div by 1000 to get sec not millis
}
mp3.Add(ob);
}
public List<string> ReturnPlayableMp3(bool sdCard)
{
List<string> res = new List<string>();
string phyle;
string path1 = null;
if(sdCard) // get mp3 from SD card
{
string baseFolderPath = "";
try
{
bool getSDPath = true;
Context context = Application.Context;
Java.IO.File[] dirs = context.GetExternalFilesDirs(null);
foreach (Java.IO.File folder in dirs)
{
bool IsRemovable = Android.OS.Environment.InvokeIsExternalStorageRemovable(folder);
bool IsEmulated = Android.OS.Environment.InvokeIsExternalStorageEmulated(folder);
if (getSDPath ? IsRemovable && !IsEmulated : !IsRemovable && IsEmulated)
baseFolderPath = folder.Path;
}
}
catch (Exception ex)
{
Console.WriteLine("GetBaseFolderPath caused the following exception: {0}", ex);
}
string xy = baseFolderPath.Remove(18); // This is result after this, but this hard coded solution could be a problem on different phones.: "/storage/05B6-2226/Android/data/Media_Player.Media_Player/files"
path1 = xy;
// path to SD card and MUSIC "/storage/05B6-2226/"
}
else // get Mp3 from internal storage
{
path1 = Android.OS.Environment.ExternalStorageDirectory.ToString();
}
var mp3Files = Directory.EnumerateFiles(path1, "*.mp3", SearchOption.AllDirectories);
foreach (string currentFile in mp3Files)
{
phyle = currentFile;
res.Add(phyle);
}
return res;
}
}
public class PhotoViewHolder : RecyclerView.ViewHolder
{
public ImageView Image { get; private set; }
public TextView Caption { get; private set; }
public PhotoViewHolder(View itemView) : base(itemView)
{
// Locate and cache view references:
Image = itemView.FindViewById<ImageView>(Resource.Id.imageView);
Caption = itemView.FindViewById<TextView>(Resource.Id.textView);
}
}
public class PhotoAlbumAdapter : RecyclerView.Adapter
{
public List<MP3object> mp3;
public PhotoAlbumAdapter(List<MP3object> mp3)
{
this.mp3 = mp3;
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.lay, parent, false);
PhotoViewHolder vh = new PhotoViewHolder(itemView);
return vh;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
PhotoViewHolder vh = holder as PhotoViewHolder;
vh.Caption.Text = mp3[position].SongName;
}
public override int ItemCount
{
get { return mp3.Count(); }
}
}
}
So getting the list of strings with the locations of the Mp3 works very quickly, but then "WriteMetaDataToFileList(obj)" kicks in, comming from "PopulateMP3List(List content)" and this is what takes so long. What I think I need is for the recyclerview to only build the first 20 objects, and when the user starts scrolling, builds the next 20 objects and attaches them to list for them to also be scrolled. Please help me out here :)
Here is an abstract class:
public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
private LinearLayoutManager linearLayoutManager;
protected PaginationScrollListener(LinearLayoutManager linearLayoutManager) {
this.linearLayoutManager = linearLayoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = linearLayoutManager.getChildCount();
int totalItemCount = linearLayoutManager.getItemCount();
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
if (!isLoading() && !isLastPage()) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount && firstVisibleItemPosition >= 0) {
loadMoreItems();
}
}
}
protected abstract void loadMoreItems();
public abstract boolean isLastPage();
public abstract boolean isLoading();
}
and In your adapter you must follow this pattern:
public class ConsultancyAdapter extends RecyclerView.Adapter<ConsultancyAdapter.ConsultancyVH> {
private static final int ITEM = 0;
private static final int LOADING = 1;
private boolean isLoadingAdded = false;
public ConsultancyAdapter(List<Consultancy> consultancies, ConsultancyAdapterListener listener) {
}
#NonNull
#Override
public ConsultancyVH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case ITEM:
viewHolder = getViewHolder(parent, layoutInflater);
break;
case LOADING:
View v2 = layoutInflater.inflate(R.layout.item_progress, parent, false);
viewHolder = new ConsultancyVH(v2);
break;
}
return (ConsultancyVH) viewHolder;
}
#NonNull
private RecyclerView.ViewHolder getViewHolder(ViewGroup parent, LayoutInflater inflater) {
RecyclerView.ViewHolder viewHolder;
View v1 = inflater.inflate(R.layout.item_consultancy, parent, false);
viewHolder = new ConsultancyVH(v1);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull ConsultancyVH holder, int position) {
Consultancy consultancy = consultancies.get(position);
switch (getItemViewType(position)) {
case ITEM:
ConsultancyVH mySingeCounseller = holder;
holder.title.setText(consultancy.getTitle()); // set cardTitle
holder.fieldArea.setText(consultancy.getField_filedoctorskills());
break;
case LOADING:
break;
}
}
#Override
public int getItemCount() {
return consultancies.size();
}
#Override
public int getItemViewType(int position) {
return (position == consultancies.size() - 1 && isLoadingAdded) ? LOADING : ITEM;
}
public void add(Consultancy mc) {
consultancies.add(mc);
notifyItemInserted(consultancies.size() - 1);
}
public void addAll(List<Consultancy> mcList) {
for (Consultancy mc : mcList) {
add(mc);
}
}
public void remove(Consultancy city) {
int position = consultancies.indexOf(city);
if (position > -1) {
consultancies.remove(position);
notifyItemRemoved(position);
}
}
public Consultancy getItem(int position) {
return consultancies.get(position);
}
public void clear() {
isLoadingAdded = false;
while (getItemCount() > 0) {
remove(getItem(0));
}
}
public boolean isEmpty() {
return getItemCount() == 0;
}
public void addLoadingFooter() {
isLoadingAdded = true;
add(new Consultancy());
}
public void removeLoadingFooter() {
isLoadingAdded = false;
int position = consultancies.size() - 1;
Consultancy item = getItem(position);
if (item != null) {
consultancies.remove(position);
notifyItemRemoved(position);
}
}
public interface ConsultancyAdapterListener {
void onCaseClicked(int position, String nid, String fieldArea, String title);
}
protected class ConsultancyVH extends RecyclerView.ViewHolder {
private TextView title, fieldArea;
private CircleImageView iconProfile;
private MaterialRippleLayout caseButtonRipple;
public ConsultancyVH(View itemView) {
super(itemView);
caseButtonRipple = itemView.findViewById(R.id.case_button_ripple);
this.title = itemView.findViewById(R.id.docName);
this.fieldArea = itemView.findViewById(R.id.fieldArea);
this.iconProfile = itemView.findViewById(R.id.icon_profile);
}
}
}
and in your activity:
private void setScrollListener() {
recyclerView.addOnScrollListener(new PaginationScrollListener(linearLayoutManager) {
#Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1;
loadNextPage();
}
#Override
public boolean isLastPage() {
return isLastPage;
}
#Override
public boolean isLoading() {
return isLoading;
}
});
loadFirstPage();
}
and in my loadFirstPage i talk to a API and you need some your code:
private void loadFirstPage() {
CallData().enqueue(new DefaultRetrofitCallback<List<Consultancy>>() {
#Override
protected void onFailure(Throwable t) {
super.onFailure(t);
}
#Override
protected void onSuccess(List<Consultancy> response) {
swipeRefreshLayout.setRefreshing(false);
dataList = response;
adapter.addAll(dataList);
recyclerView.setAdapter(adapter);
if (!checkLast(response)) adapter.addLoadingFooter();
else isLastPage = true;
}
#Override
protected void onOtherStatus(Response<List<Consultancy>> response) {
super.onOtherStatus(response);
}
#Override
protected void always() {
super.always();
}
});
}
and loadNextPage:
private void loadNextPage() {
CallData().enqueue(new DefaultRetrofitCallback<List<Consultancy>>() {
#Override
protected void onFailure(Throwable t) {
super.onFailure(t);
}
#Override
protected void onSuccess(List<Consultancy> response) {
swipeRefreshLayout.setRefreshing(false);
adapter.removeLoadingFooter();
isLoading = false;
swipeRefreshLayout.setRefreshing(false);
adapter.addAll(response);
if (!checkLast(response)) adapter.addLoadingFooter();
else isLastPage = true;
}
#Override
protected void onOtherStatus(Response<List<Consultancy>> response) {
super.onOtherStatus(response);
}
#Override
protected void always() {
super.always();
}
});
}
I haven't found yet the proper examples of how to make an app load more items from Retrofit onResponse while scrolling down the RecyclerView.
Here is how I'm loading my fist 20 items:
List<ModelPartners> model = new ArrayList<>();
Call<ResponsePartners> call = ApiClient.getRetrofit().getPartnerList(params);
call.enqueue(this);
My RecyclerView
PartnersAdapter adapter = new PartnersAdapter(getContext(), recyclerView, model);
recyclerView.setAdapter(adapter);
And here is my Retrofit onResponse:
#Override
public void onResponse(Call<ResponsePartners> call, Response<ResponsePartners> response) {
if (getActivity() != null && response.isSuccessful()) {
List<ModelPartners> body = response.body().getData();
//Rest of the code to add body to my Adapter and notifyDataSetChanged
}
}
My Problem is, every time I add the method to load more items on scroll, onResponse keeps loading non-stop till the last page. Even if I have put set loading to false.
Can someone please show how to properly use pagination in Retrofit? What libraries you used with Retrofit or show me how you did it your way? Thank you in advance
You need three things to achieve this, you need:
You need an onscroll listener for the recycler view.
You need a method to add new items to the recycler adapter
You need to call your paginated endpoint
Assuming you have a model like this from the server
public class PagedList<T> {
private int page = 0;
private List<T> results = new ArrayList<>();
private int totalResults = 0;
private int totalPages = 0;
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public List<T> getResults() {
return results;
}
public void setResults(List<T> results) {
this.results = results;
}
public int getTotalResults() {
return totalResults;
}
public void setTotalResults(int totalResults) {
this.totalResults = totalResults;
}
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
}
Your OnScroll Listener: -
In your activity this is what you would have, so add the second method to your activity
public void onCreate(Bundle savedInstance) {
// setup your view components
// ...
// get the layout manager
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
// rest endpoint
apiEndpoint = retrofit.create(RetrofitEndpoint.class);
// initialise loading state
mIsLoading = false;
mIsLastPage = false;
// amount of items you want to load per page
final int pageSize = 20;
// set up scroll listener
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// number of visible items
int visibleItemCount = layoutManager.getChildCount();
// number of items in layout
int totalItemCount = layoutManager.getItemCount();
// the position of first visible item
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
boolean isNotLoadingAndNotLastPage = !mIsLoading && !mIsLastPage;
// flag if number of visible items is at the last
boolean isAtLastItem = firstVisibleItemPosition + visibleItemCount >= totalItemCount;
// validate non negative values
boolean isValidFirstItem = firstVisibleItemPosition >= 0;
// validate total items are more than possible visible items
boolean totalIsMoreThanVisible = totalItemCount >= pageSize;
// flag to know whether to load more
boolean shouldLoadMore = isValidFirstItem && isAtLastItem && totalIsMoreThanVisible && isNotLoadingAndNotLastPage
if (shouldLoadMore) loadMoreItems(false);
}
});
// load the first page
loadMoreItems(true);
}
private void loadMoreItems(boolean isFirstPage) {
// change loading state
mIsLoading = true;
mCurrentPage = mCurrentPage + 1;
// update recycler adapter with next page
apiEndpoint.getPagedList(mCurrentPage).enqueue(new Callback<PagedList<Object>>() {
#Override
public void onResponse(Call<PagedList<Object>> call, Response<PagedList<Object>> response) {
PagedList<Object> result = response.body();
if (result == null) return;
else if (!isFirstPage) mAdapter.addAll(result.getResults());
else mAdapter.setList(result.getResults());
mIsLoading = false;
mIsLastPage = mCurrentPage == result.getTotalPages();
}
#Override
public void onFailure(Call<PagedList<Object>> call, Throwable t) {
Log.e("SomeActivity", t.getMessage());
}
});
}
Recycler Adapter: -
For the recycler adapter all you need is to add a method that adds items to its list, as below
public class SomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Object> list;
// .. other overridden members
public void setList(List<Object> list) {
this.list = list;
notifyDataSetChanged();
}
public void addAll(List<Object> newList) {
int lastIndex = list.size() - 1;
list.addAll(newList);
notifyItemRangeInserted(lastIndex, newList.size());
}
}
Then finally your retrofit interface that takes the page, as below:
interface RetrofitEndpoint {
#GET("paged/list/endpoint")
Call<PagedList<Object>> getPagedList(#Query("page") int page);
}
Hope that helps.
#Alimov Shohrukh, I also tried for many ways for Pagination
1) Check this link,This is one way
2) I mention it step by step
from API side - you need to pass pageConunt, pageIndex and get data based on above parameters
pageConunt - means how many data you want get from server for i.e - 10
pageIndex - page number for i.e 1,2,3 etc
public class DemoActivity extends BaseActivity{
List<BaseModel> orderList = new ArrayList<>();
ListAdapter listAdapter;
LinearLayoutManager layoutManager;
OrderListAdapter orderListAdapter;
int pageIndex = 1; // you can pass 1,2,3...
int pageCount = 10; //you can pass 10,20...
boolean isApiSuccess = false;
//if api get success == true then you need to work with load more sp we take one boolean variable
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
findView();
setRecyclerViewPagination();
}
private void findView() {
//recyclerview
rv_po_order_number = findViewById(R.id.rv_po_order_number);
}
//custom OnScrollListener
private RecyclerView.OnScrollListener recyclerViewOnScrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (isApiSuccess) {
pageIndex++;
listTestApiCall(pageIndex, searchQuery);
Log.e(TAG, pageIndex + " page count ");
orderListAdapter.showLoading(true);
rv_po_order_number.post(new Runnable() {
public void run() {
// There is no need to use notifyDataSetChanged()
orderListAdapter.notifyDataSetChanged();
}
});
}
}
};
private void setRecyclerViewPagination() {
orderList = new ArrayList<>();
orderListAdapter = new OrderListAdapter(mActivity, orderList);
layoutManager = new LinearLayoutManager(mActivity, LinearLayoutManager.VERTICAL, false);
rv_po_order_number.setLayoutManager(layoutManager);
rv_po_order_number.setHasFixedSize(true);
rv_po_order_number.setAdapter(orderListAdapter);
rv_po_order_number.addOnScrollListener(recyclerViewOnScrollListener);
listTestApiCall(pageCount,pageIndex);
}
private void listTestApiCall(final int pageCount,final int pageIndex) {
// if (CommonUtils.isConnectingToInternet(mActivity)) {
if (validateInternetConn(mActivity)) {//check internet
final boolean isFirstPage = pageIndex == 1 ? true : false;
if (isFirstPage)
//do your stuff - if you want to show loader
Call<BaseModel> call = ApiClient.getClient().listApiCall(pageCount, pageIndex);
call.enqueue(new Callback<BaseModel>() {
#Override
public void onResponse(Call<BaseModel> call, Response<BaseModel> response) {
try {
if (response.isSuccessful()) {
boolean success = response.body().getStatus();
isApiSuccess = response.body().getStatus();
if (success) {
List<BaseModel> list = new ArrayList<>();
if (response.body().getData() != null) {
list = response.body().getData();
if (isFirstPage)
orderList.clear();
orderList.addAll(response.body().getData());
}
if (list != null && list.size() > 0) {
isApiSuccess = true;
} else {
isApiSuccess = false;
}
} else if (!success) {
isApiSuccess = false;
String message = response.body().getMessage();
}
listAdapter.showLoading(false);
listAdapter.notifyDataSetChanged();
}
} catch (Exception e) {
e.printStackTrace();
Log.e("Tag", "error=" + e.toString());
isApiSuccess = false;
}
}
#Override
public void onFailure(Call<BaseModel> call, Throwable t) {
Log.e("Tag", "error" + t.toString());
isApiSuccess = false;
}
});
}
}
}
here is OrderListAdapter
public class OrderListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
Activity mActivity;
List<OrderListBaseModel.DataBean> mList;
PurchaseOrderReceiveOnClick purchaseOrderReceiveOnClick;
DeleteUpdatePOOnClick deleteUpdatePOOnClick;
public static final int TYPE_FOOTER = 1;
public static final int TYPE_ITEM = 2;
protected boolean showLoader = true;
public OrderListAdapter(Activity mActivity, List<OrderListBaseModel.DataBean> mList, PurchaseOrderReceiveOnClick purchaseOrderReceiveOnClick,DeleteUpdatePOOnClick deleteUpdatePOOnClick) {
this.mActivity = mActivity;
this.purchaseOrderReceiveOnClick = purchaseOrderReceiveOnClick;
this.deleteUpdatePOOnClick = deleteUpdatePOOnClick;
this.mList = mList;
}
public OrderListAdapter(Activity mActivity, List<OrderListBaseModel.DataBean> mList) {
this.mActivity = mActivity;
this.mList = mList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_purchase_order_receive, parent, false);
return new DataViewHolder(v, purchaseOrderReceiveOnClick);
} else if (viewType == TYPE_FOOTER) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.loading_layout, parent, false);
return new FooterViewHolder(v);
} else
return null;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof DataViewHolder) {
final DataViewHolder dataViewHolder = (DataViewHolder) holder;
final OrderListBaseModel.DataBean dataBean = getItem(position);
if (dataBean != null) {
dataViewHolder.setPosition(position);
dataViewHolder.setSingleBean(dataBean);
}
}
}
private OrderListBaseModel.DataBean getItem(int position) {
if (mList.size() > 0)
return mList.get(position);
else
return null;
}
public void showLoading(boolean status) {
showLoader = status;
}
#Override
public int getItemViewType(int position) {
if ((position == mList.size() - 1) && showLoader) {
return TYPE_FOOTER;
}
return TYPE_ITEM;
}
public void removeItem(int position) {
mList.remove(position);
notifyItemRemoved(position);
}
#Override
public int getItemCount() {
return mList.size();
}
public static class DataViewHolder extends RecyclerView.ViewHolder {
int position;
OrderListBaseModel.DataBean dataBean;
private TextView tv_row_po_no,textViewOptions;
public DataViewHolder(View v, final PurchaseOrderReceiveOnClick purchaseOrderReceiveOnClick) {
super(v);
tv_row_po_no = v.findViewById(R.id.tv_row_po_no);
textViewOptions = v.findViewById(R.id.textViewOptions);
}
public void setPosition(int position) {
this.position = position;
}
public void setSingleBean(OrderListBaseModel.DataBean dataBean) {
this.dataBean = dataBean;
}
}
private class FooterViewHolder extends RecyclerView.ViewHolder {
public FooterViewHolder(View view) {
super(view);
}
}
}
here is xml code
loading_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/linearRoot"
android:gravity="center_horizontal"
android:layout_marginTop="#dimen/_5sdp">
<com.app.trackpoint.custom_loader.MYLoader
android:layout_width="#dimen/_30sdp"
app:my_color="#color/colorAccent"
android:layout_height="#dimen/_30sdp" />
</LinearLayout>
i m integrate recycleview with multiple view so below is my code
public class ActivityProdcutListWithMulipleView extends AppCompatActivity {
RecyclerView mRecyclerView;
Toolbar mToolbar;
PostParseGet mPostParseGet;
AllMethods mAllMethods;
GetProductListing mGetProductListing;
RecyclerView.LayoutManager mLayoutManager;
ProgressDialog mProgressDialog;
int page = 1;
int total = 0;
int totalFeature = 0;
int totalbanner = 0;
ImageView mImageViewBack;
ArrayList<GetProductListing.ListingBlock> mListingBlocks;
ArrayList<GetProductListing.ListingBlock> mListingBlocksFeature;
ArrayList<GetProductListing.ListingBlock> mListingBlocksBanner;
ArrayList<OrderItemDemo> mOrderItemDemos = new ArrayList<OrderItemDemo>();
GridLayoutManager mLayoutManagerGrid;
DifferentRowAdapter adapter;
private Handler handler;
ArrayList<GetProductListing.ListingBlock> adapterData;
public static Activity mActivity;
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_product_list);
mListingBlocks = new ArrayList<>();
mActivity = this;
mListingBlocksFeature = new ArrayList<>();
mListingBlocksBanner = new ArrayList<>();
mRecyclerView = (RecyclerView) findViewById(R.id.recycle);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mImageViewBack = (ImageView) mToolbar.findViewById(R.id.imgBack);
WebAPIApplication.mArrayListingAllProductBlocks.clear();
mPostParseGet = new PostParseGet(ActivityProdcutListWithMulipleView.this);
mAllMethods = new AllMethods(ActivityProdcutListWithMulipleView.this);
mGetProductListing = new GetProductListing();
mLayoutManagerGrid = new GridLayoutManager(ActivityProdcutListWithMulipleView.this, 2);
mLayoutManager = new LinearLayoutManager(ActivityProdcutListWithMulipleView.this);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(mLayoutManagerGrid);
handler = new Handler();
adapterData = new ArrayList<>();
mRecyclerView.addOnScrollListener(new EndlessRecyclerGridOnScrollListener(mLayoutManagerGrid) {
#Override
public void onLoadMore(int current_page) {
if (WebAPIApplication.mArrayListingAllProductBlocks.size() < total) {
page++;
new getProductData(false, true).execute();
}
}
});
adapter = new DifferentRowAdapter(mOrderItemDemos);
mRecyclerView.setAdapter(adapter);
mLayoutManagerGrid.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
return adapter.getItemViewType(position) == OrderItemDemo.BANNER_TYPE ? 2 : 1;
}
});
if (mAllMethods.check_Internet() == true) {
new getProductData(true, false).execute();
} else {
mAllMethods.ShowDialog(ActivityProdcutListWithMulipleView.this, "", getString(R.string.net_not_available), "OK");
}
mImageViewBack.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
}
public static class DifferentRowAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
boolean isLoading = false, isMoreDataAvailable = true;
private List<OrderItemDemo> mList;
OnLoadMoreListener loadMoreListener;
public DifferentRowAdapter(List<OrderItemDemo> list) {
this.mList = list;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case OrderItemDemo.NORMAL_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_list_file, parent, false);
return new ProductListRow(view);
case OrderItemDemo.FEATURE_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_list_feature_file, parent, false);
return new ProductListRow(view);
case OrderItemDemo.BANNER_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_list_file, parent, false);
return new ProductListRow(view);
}
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (position >= getItemCount() - 1 && isMoreDataAvailable && !isLoading && loadMoreListener != null) {
isLoading = true;
loadMoreListener.onLoadMore();
}
final OrderItemDemo object = mList.get(position);
if (object != null) {
switch (object.getmType()) {
case OrderItemDemo.NORMAL_TYPE:
((ProductListRow) holder).name.setText(object.getId() + " " + object.getTitle());
((ProductListRow) holder).price.setText(object.getType());
Picasso.with(mActivity)
.load(object.getImage())
.into(((ProductListRow) holder).mImageViewProduct);
((ProductListRow) holder).mImageViewBanner.setVisibility(View.GONE);
((ProductListRow) holder).mRelativeLayout.setVisibility(View.VISIBLE);
break;
case OrderItemDemo.FEATURE_TYPE:
((ProductListRow) holder).name.setText(object.getId() + " " + object.getTitle());
((ProductListRow) holder).price.setText(object.getType());
((ProductListRow) holder).mImageViewBanner.setVisibility(View.GONE);
((ProductListRow) holder).mRelativeLayout.setVisibility(View.VISIBLE);
Picasso.with(mActivity)
.load(object.getImage())
.into(((ProductListRow) holder).mImageViewProduct);
break;
case OrderItemDemo.BANNER_TYPE:
((ProductListRow) holder).price.setText(object.getType());
((ProductListRow) holder).mImageViewBanner.setVisibility(View.VISIBLE);
((ProductListRow) holder).mRelativeLayout.setVisibility(View.GONE);
break;
}
}
}
public void setMoreDataAvailable(boolean moreDataAvailable) {
isMoreDataAvailable = moreDataAvailable;
}
public void notifyDataChanged() {
notifyDataSetChanged();
isLoading = false;
}
interface OnLoadMoreListener {
void onLoadMore();
}
public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) {
this.loadMoreListener = loadMoreListener;
}
#Override
public int getItemCount() {
if (mList == null)
return 0;
return mList.size();
}
#Override
public int getItemViewType(int position) {
if (mList != null) {
OrderItemDemo object = mList.get(position);
if (object != null) {
return object.getmType();
}
}
return 0;
}
}
public abstract class EndlessRecyclerGridOnScrollListener extends
RecyclerView.OnScrollListener {
public String TAG = EndlessRecyclerGridOnScrollListener.class
.getSimpleName();
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;
private int current_page = 1;
private LinearLayoutManager mLinearLayoutManager;
public EndlessRecyclerGridOnScrollListener(
GridLayoutManager 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();
visibleThreshold = total;
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);
}
public class getProductData extends AsyncTask<Void, Void, Void> {
boolean showLoading;
boolean pagination;
public getProductData(boolean showLoading, boolean page) {
this.showLoading = showLoading;
this.pagination = page;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
if (showLoading == true) {
mProgressDialog = ProgressDialog.show(ActivityProdcutListWithMulipleView.this, "", "Loading..");
}
}
#Override
protected Void doInBackground(Void... voids) {
mGetProductListing = (GetProductListing) mPostParseGet.getTopSearchData(mGetProductListing, page);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (showLoading == true) {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
}
if (mGetProductListing != null) {
try {
total = Integer.parseInt(mGetProductListing.getListing().getTotal());
if (mGetProductListing.getListing().getData().size() > 0) {
mListingBlocks = getProductBlock();
Log.e("FeatureSize", String.valueOf(mListingBlocksFeature.size()));
Log.e("BannerSize", String.valueOf(mListingBlocksBanner.size()));
for (int i = 0; i < WebAPIApplication.mArrayListingAllProductBlocks.size(); i++) {
if (WebAPIApplication.mArrayListingAllProductBlocks.get(i).getType().equalsIgnoreCase("product")) {
mOrderItemDemos.add(new OrderItemDemo(WebAPIApplication.mArrayListingAllProductBlocks.get(i).getId(), WebAPIApplication.mArrayListingAllProductBlocks.get(i).getTitle(), WebAPIApplication.mArrayListingAllProductBlocks.get(i).getType(), WebAPIApplication.mArrayListingAllProductBlocks.get(i).getMedium_image(), OrderItemDemo.NORMAL_TYPE));
} else if (WebAPIApplication.mArrayListingAllProductBlocks.get(i).getType().equalsIgnoreCase("featured")) {
mOrderItemDemos.add(new OrderItemDemo(WebAPIApplication.mArrayListingAllProductBlocks.get(i).getId(), WebAPIApplication.mArrayListingAllProductBlocks.get(i).getTitle(), WebAPIApplication.mArrayListingAllProductBlocks.get(i).getType(), WebAPIApplication.mArrayListingAllProductBlocks.get(i).getMedium_image(), OrderItemDemo.FEATURE_TYPE));
} else {
mOrderItemDemos.add(new OrderItemDemo(WebAPIApplication.mArrayListingAllProductBlocks.get(i).getId(), WebAPIApplication.mArrayListingAllProductBlocks.get(i).getTitle(), WebAPIApplication.mArrayListingAllProductBlocks.get(i).getType(), WebAPIApplication.mArrayListingAllProductBlocks.get(i).getMedium_image(), OrderItemDemo.BANNER_TYPE));
}
}
adapter = new DifferentRowAdapter(mOrderItemDemos);
if (pagination == false) {
mRecyclerView.setAdapter(adapter);
}
adapter.notifyDataSetChanged();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/*Get Product data */
public ArrayList<GetProductListing.ListingBlock> getProductBlock() {
ArrayList<GetProductListing.ListingBlock> mArrayListParserLog = new ArrayList<GetProductListing.ListingBlock>();
for (int i = 0; i < mGetProductListing.getListing().getData().size(); i++) {
GetProductListing.ListingBlock mParserLog = new GetProductListing.ListingBlock();
mParserLog.setId(mGetProductListing.getListing().getData().get(i).getId());
mParserLog.setType(mGetProductListing.getListing().getData().get(i).getType());
mParserLog.setTitle(mGetProductListing.getListing().getData().get(i).getTitle());
mParserLog.setDiscountSale(mGetProductListing.getListing().getData().get(i).getDiscountSale());
mParserLog.setDiscountSelling(mGetProductListing.getListing().getData().get(i).getDiscountSelling());
mParserLog.setPrice(mGetProductListing.getListing().getData().get(i).getPrice());
mParserLog.setAupc(mGetProductListing.getListing().getData().get(i).getAupc());
mParserLog.setSale_end(mGetProductListing.getListing().getData().get(i).getSale_end());
mParserLog.setSale_start(mGetProductListing.getListing().getData().get(i).getSale_start());
mParserLog.setSelling_price(mGetProductListing.getListing().getData().get(i).getSelling_price());
mParserLog.setProduct_rating(mGetProductListing.getListing().getData().get(i).getProduct_rating());
mParserLog.setSlug(mGetProductListing.getListing().getData().get(i).getSlug());
mParserLog.setSold_out(mGetProductListing.getListing().getData().get(i).getSold_out());
mParserLog.setImage(mGetProductListing.getListing().getData().get(i).getImage());
mParserLog.setSku_id(mGetProductListing.getListing().getData().get(i).getSku_id());
mParserLog.setMedium_image(mGetProductListing.getListing().getData().get(i).getMedium_image());
mArrayListParserLog.add(mParserLog);
WebAPIApplication.mArrayListingAllProductBlocks.add(mParserLog);
}
return mArrayListParserLog;
}
}
When i run above code in first page i get proper value but when i call second page at that time first page value will append again with second page data so any idea how can i solve this ? your all suggestions are appreciable
Try arraylist.clear() before loading Updated data into ArrayList to Avoid Duplicate Values.
1.Add this Code Before Oncreate
ArrayList<GetProductListing.ListingBlock> mArrayListParserLog = new ArrayList<GetProductListing.ListingBlock>();
and remove it from getProductBlock()
Add mArrayListParserLog.clear(); with in getProductBlock() method
public ArrayList<GetProductListing.ListingBlock> getProductBlock(){
mArrayListParserLog.clear();
I want develop android application for one website. I read website posts from json and show its in RecyclerView every 10 posts and when user scrolling on RecyclerView show more 10 post and go to end! in this project i use okHTTP v3 and RecyclerView!
I want gonna put a button and when click on this button, load new data (if there was new data).
Button codes:
// Refresh Data
toolbar_refresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LoadData();
}
});
}
Main Activity codes:
public class Main_page extends AppCompatActivity {
private static final long RIPPLE_DURATION = 250;
private Toolbar toolbar;
private RelativeLayout root;
private ImageView menu_image, toolbar_refresh;
private RecyclerView main_recyclerView;
private MainAdapter_loadMore mAdaper;
private List<MainDataModel> dataModels = new ArrayList<MainDataModel>();
protected Handler handler;
private RelativeLayout loadLayout;
private LinearLayoutManager mLayoutManager;
private int pageCount = 1;
String ServerAddress = ServerIP.getIP();
private Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_page);
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
// Init
handler = new Handler();
context = getApplicationContext();
toolbar = (Toolbar) findViewById(R.id.main_toolbar);
mLayoutManager = new LinearLayoutManager(this);
loadLayout = (RelativeLayout) findViewById(R.id.main_empty_layout);
toolbar_refresh = (ImageView) toolbar.findViewById(R.id.toolbar_update);
// Toolbar
if (toolbar != null) {
setSupportActionBar(toolbar);
getSupportActionBar().setTitle(null);
}
// Load First Data
LoadData();
// Menu
root = (RelativeLayout) findViewById(R.id.main_root);
View guillotineMenu = LayoutInflater.from(this).inflate(R.layout.menu_layout, null);
root.addView(guillotineMenu);
menu_image = (ImageView) toolbar.findViewById(R.id.toolbar_logo);
new GuillotineAnimation.GuillotineBuilder(guillotineMenu, guillotineMenu.findViewById(R.id.menu_layout_image), menu_image)
.setStartDelay(RIPPLE_DURATION)
.setActionBarViewForAnimation(toolbar)
.setClosedOnStart(true)
.build();
// RecyclerView and setData
main_recyclerView = (RecyclerView) findViewById(R.id.main_recycler);
main_recyclerView.setHasFixedSize(true);
main_recyclerView.setLayoutManager(mLayoutManager);
mAdaper = new MainAdapter_loadMore(this, main_recyclerView, dataModels);
main_recyclerView.setAdapter(mAdaper);
// Load More data
mAdaper.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
dataModels.add(null);
mAdaper.notifyItemInserted(dataModels.size() - 1);
LoadMoreData(pageCount);
}
});
// Refresh Data
toolbar_refresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LoadData();
}
});
}
#Subscribe
public void onEvent(List<MainDataModel> mainInfoModels) {
if (dataModels.size() > 0) {
dataModels.remove(dataModels.size() - 1);
mAdaper.notifyItemRemoved(dataModels.size());
mAdaper.setLoaded();
}
mAdaper.add(mainInfoModels);
mAdaper.notifyDataSetChanged();
pageCount++;
if (dataModels.isEmpty()) {
main_recyclerView.setVisibility(View.GONE);
loadLayout.setVisibility(View.VISIBLE);
} else {
main_recyclerView.setVisibility(View.VISIBLE);
loadLayout.setVisibility(View.GONE);
}
}
private void LoadData() {
MainDataInfo dataInfo = new MainDataInfo();
// here getMainDataInfo() should return the server response
dataInfo.getMainDataInfo(this);
}
private void LoadMoreData(int pageNumber) {
MainDataInfo_loadMore dataInfo_loadMore = new MainDataInfo_loadMore();
// here getMainDataInfo() should return the server response
dataInfo_loadMore.getMainDataInfo_loadMore(this, pageNumber);
}
}
Adapter codes:
public class MainAdapter_loadMore extends RecyclerView.Adapter {
private List<MainDataModel> mDateSet;
private Context mContext;
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public MainAdapter_loadMore(Context context, RecyclerView recyclerView, List<MainDataModel> dataSet) {
this.mContext = context;
this.mDateSet = dataSet;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
}
#Override
public int getItemViewType(int position) {
return mDateSet.get(position) != null ? VIEW_ITEM : VIEW_PROG;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.post_card_layout, parent, false);
vh = new DataViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progressbar_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof DataViewHolder) {
((DataViewHolder) holder).main_post_title.setText(mDateSet.get(position).getTitle());
Glide.with(mContext)
.load(mDateSet.get(position).getThumbnail())
.placeholder(R.drawable.post_image)
.crossFade()
.into(((DataViewHolder) holder).main_post_image);
((DataViewHolder) holder).main_post_content.setText(Html.fromHtml(mDateSet.get(position).getContent()));
} else {
((ProgressViewHolder) holder).progressBar.setVisibility(View.VISIBLE);
}
}
public void setLoaded() {
loading = false;
}
#Override
public int getItemCount() {
return mDateSet.size();
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public void remove(int position) {
mDateSet.remove(position);
notifyItemRemoved(position);
}
public void clear() {
mDateSet.clear();
notifyDataSetChanged();
}
public void add(List<MainDataModel> models) {
mDateSet.addAll(models);
notifyDataSetChanged();
}
public void update(List<MainDataModel> models) {
mDateSet.clear();
mDateSet.addAll(models);
notifyDataSetChanged();
}
public class DataViewHolder extends RecyclerView.ViewHolder {
private TextView main_post_title, main_post_content;
private ImageView main_post_image;
public DataViewHolder(View itemView) {
super(itemView);
main_post_title = (TextView) itemView.findViewById(R.id.post_content_title);
main_post_image = (ImageView) itemView.findViewById(R.id.post_picture_image);
main_post_content = (TextView) itemView.findViewById(R.id.post_content_text);
}
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
public AVLoadingIndicatorView progressBar;
public ProgressViewHolder(View v) {
super(v);
progressBar = (AVLoadingIndicatorView) v.findViewById(R.id.avloadingIndicatorView);
}
}
}
AsyncTask code (LoadData codes) :
public class MainDataInfo {
private Context mContext;
private String ServerAddress = ServerIP.getIP();
public void getMainDataInfo(Context context) {
mContext = context;
new getInfo().execute(ServerAddress + "page=1");
}
private class getInfo extends AsyncTask<String, Void, String> {
EventBus bus = EventBus.getDefault();
private String ou_response;
private List<MainDataModel> infoModels;
#Override
protected void onPreExecute() {
CustomProcessDialog.createAndShow(mContext);
infoModels = new ArrayList<>();
}
#Override
protected String doInBackground(String... params) {
OkHttpClient client = new OkHttpClient();
//String url = (String) params[0];
Request request = new Request.Builder()
.url(ServerAddress + "page=1")
.cacheControl(CacheControl.FORCE_NETWORK)
.build();
Response response;
try {
response = client.newCall(request).execute();
ou_response = response.body().string();
response.body().close();
if (ou_response != null) {
try {
JSONObject postObj = new JSONObject(ou_response);
JSONArray postsArray = postObj.optJSONArray("posts");
infoModels = new ArrayList<>();
for (int i = 0; i <= infoModels.size(); i++) {
JSONObject postObject = (JSONObject) postsArray.get(i);
JSONObject images = postObject.optJSONObject("thumbnail_images");
JSONObject imagesPair = images.optJSONObject("medium");
int id = postObject.getInt("id");
String title = postObject.getString("title");
String content = postObject.getString("content");
String thumbnail = imagesPair.getString("url");
Log.d("Data", "Post id: " + id);
Log.d("Data", "Post title: " + title);
Log.d("Data", "Post title: " + thumbnail);
//Use the title and id as per your requirement
infoModels.add(new MainDataModel(id, title, content, thumbnail));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return ou_response;
}
#Override
protected void onPostExecute(String result) {
CustomProcessDialog.dissmis();
if (result != null) {
bus.post(infoModels);
}
}
}
}
When use this method LoadData(); in button action, not load new data just copy fist 10 post and load again this first 10 post!
How can i fix it and when click on Button click action, load new data ?
Attention : Please don't give me negative points, help me and i really need you helps! thanks all <3
Add this in your MainDataInfo class:
public class MainDataInfo {
private MainDataListener mListener;
public interface MainDataListener {
void onDataReceived(List<MainDataModel> infoModels);
}
public void setDataListener(MainDataListener listener){
this.mListener = listener;
}
#Override
protected void onPostExecute(String result) {
CustomProcessDialog.dissmis();
if (result != null) {
bus.post(infoModels);
//add this
if(mListener !=null)
mListener.onDataReceived(infoModels);
}
}
}
In your activity Main_page:
//implement the maindata listener
public class Main_page extends AppCompatActivity implement MainDataInfo.MainDataListener { }
Override the interface's method in the activity class
#Override
void onDataReceived(List<MainDataModel> infoModels){
// here set your recyclerview adapter
mAdaper.update(infoModels);
mAdaper.notifyDataSetChanged();
}
I want develop android application for one website. I read website posts from json and show its in RecyclerView every 10 posts and when user scrolling on RecyclerView show more 10 post and go to end! in this project i use okHTTP v3 and RecyclerView!
I want gonna put a button and when click on this button, load new data (if there was new data).
Button codes:
// Refresh Data
toolbar_refresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LoadData();
}
});
}
Main Activity codes:
public class Main_page extends AppCompatActivity {
private static final long RIPPLE_DURATION = 250;
private Toolbar toolbar;
private RelativeLayout root;
private ImageView menu_image, toolbar_refresh;
private RecyclerView main_recyclerView;
private MainAdapter_loadMore mAdaper;
private List<MainDataModel> dataModels = new ArrayList<MainDataModel>();
protected Handler handler;
private RelativeLayout loadLayout;
private LinearLayoutManager mLayoutManager;
private int pageCount = 1;
String ServerAddress = ServerIP.getIP();
private Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_page);
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
// Init
handler = new Handler();
context = getApplicationContext();
toolbar = (Toolbar) findViewById(R.id.main_toolbar);
mLayoutManager = new LinearLayoutManager(this);
loadLayout = (RelativeLayout) findViewById(R.id.main_empty_layout);
toolbar_refresh = (ImageView) toolbar.findViewById(R.id.toolbar_update);
// Toolbar
if (toolbar != null) {
setSupportActionBar(toolbar);
getSupportActionBar().setTitle(null);
}
// Load First Data
LoadData();
// Menu
root = (RelativeLayout) findViewById(R.id.main_root);
View guillotineMenu = LayoutInflater.from(this).inflate(R.layout.menu_layout, null);
root.addView(guillotineMenu);
menu_image = (ImageView) toolbar.findViewById(R.id.toolbar_logo);
new GuillotineAnimation.GuillotineBuilder(guillotineMenu, guillotineMenu.findViewById(R.id.menu_layout_image), menu_image)
.setStartDelay(RIPPLE_DURATION)
.setActionBarViewForAnimation(toolbar)
.setClosedOnStart(true)
.build();
// RecyclerView and setData
main_recyclerView = (RecyclerView) findViewById(R.id.main_recycler);
main_recyclerView.setHasFixedSize(true);
main_recyclerView.setLayoutManager(mLayoutManager);
mAdaper = new MainAdapter_loadMore(this, main_recyclerView, dataModels);
main_recyclerView.setAdapter(mAdaper);
// Load More data
mAdaper.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
dataModels.add(null);
mAdaper.notifyItemInserted(dataModels.size() - 1);
LoadMoreData(pageCount);
}
});
// Refresh Data
toolbar_refresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LoadData();
}
});
}
#Subscribe
public void onEvent(List<MainDataModel> mainInfoModels) {
if (dataModels.size() > 0) {
dataModels.remove(dataModels.size() - 1);
mAdaper.notifyItemRemoved(dataModels.size());
mAdaper.setLoaded();
}
mAdaper.add(mainInfoModels);
mAdaper.notifyDataSetChanged();
pageCount++;
if (dataModels.isEmpty()) {
main_recyclerView.setVisibility(View.GONE);
loadLayout.setVisibility(View.VISIBLE);
} else {
main_recyclerView.setVisibility(View.VISIBLE);
loadLayout.setVisibility(View.GONE);
}
}
private void LoadData() {
MainDataInfo dataInfo = new MainDataInfo();
// here getMainDataInfo() should return the server response
dataInfo.getMainDataInfo(this);
}
private void LoadMoreData(int pageNumber) {
MainDataInfo_loadMore dataInfo_loadMore = new MainDataInfo_loadMore();
// here getMainDataInfo() should return the server response
dataInfo_loadMore.getMainDataInfo_loadMore(this, pageNumber);
}
}
Adapter codes:
public class MainAdapter_loadMore extends RecyclerView.Adapter {
private List<MainDataModel> mDateSet;
private Context mContext;
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public MainAdapter_loadMore(Context context, RecyclerView recyclerView, List<MainDataModel> dataSet) {
this.mContext = context;
this.mDateSet = dataSet;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
}
#Override
public int getItemViewType(int position) {
return mDateSet.get(position) != null ? VIEW_ITEM : VIEW_PROG;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.post_card_layout, parent, false);
vh = new DataViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progressbar_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof DataViewHolder) {
((DataViewHolder) holder).main_post_title.setText(mDateSet.get(position).getTitle());
Glide.with(mContext)
.load(mDateSet.get(position).getThumbnail())
.placeholder(R.drawable.post_image)
.crossFade()
.into(((DataViewHolder) holder).main_post_image);
((DataViewHolder) holder).main_post_content.setText(Html.fromHtml(mDateSet.get(position).getContent()));
} else {
((ProgressViewHolder) holder).progressBar.setVisibility(View.VISIBLE);
}
}
public void setLoaded() {
loading = false;
}
#Override
public int getItemCount() {
return mDateSet.size();
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public void remove(int position) {
mDateSet.remove(position);
notifyItemRemoved(position);
}
public void clear() {
mDateSet.clear();
notifyDataSetChanged();
}
public void add(List<MainDataModel> models) {
mDateSet.addAll(models);
notifyDataSetChanged();
}
public void update(List<MainDataModel> models) {
mDateSet.clear();
mDateSet.addAll(models);
notifyDataSetChanged();
}
public class DataViewHolder extends RecyclerView.ViewHolder {
private TextView main_post_title, main_post_content;
private ImageView main_post_image;
public DataViewHolder(View itemView) {
super(itemView);
main_post_title = (TextView) itemView.findViewById(R.id.post_content_title);
main_post_image = (ImageView) itemView.findViewById(R.id.post_picture_image);
main_post_content = (TextView) itemView.findViewById(R.id.post_content_text);
}
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
public AVLoadingIndicatorView progressBar;
public ProgressViewHolder(View v) {
super(v);
progressBar = (AVLoadingIndicatorView) v.findViewById(R.id.avloadingIndicatorView);
}
}
}
AsyncTask code (LoadData codes) :
public class MainDataInfo {
private Context mContext;
private String ServerAddress = ServerIP.getIP();
public void getMainDataInfo(Context context) {
mContext = context;
new getInfo().execute(ServerAddress + "page=1");
}
private class getInfo extends AsyncTask<String, Void, String> {
EventBus bus = EventBus.getDefault();
private String ou_response;
private List<MainDataModel> infoModels;
#Override
protected void onPreExecute() {
CustomProcessDialog.createAndShow(mContext);
infoModels = new ArrayList<>();
}
#Override
protected String doInBackground(String... params) {
OkHttpClient client = new OkHttpClient();
//String url = (String) params[0];
Request request = new Request.Builder()
.url(ServerAddress + "page=1")
.cacheControl(CacheControl.FORCE_NETWORK)
.build();
Response response;
try {
response = client.newCall(request).execute();
ou_response = response.body().string();
response.body().close();
if (ou_response != null) {
try {
JSONObject postObj = new JSONObject(ou_response);
JSONArray postsArray = postObj.optJSONArray("posts");
infoModels = new ArrayList<>();
for (int i = 0; i <= infoModels.size(); i++) {
JSONObject postObject = (JSONObject) postsArray.get(i);
JSONObject images = postObject.optJSONObject("thumbnail_images");
JSONObject imagesPair = images.optJSONObject("medium");
int id = postObject.getInt("id");
String title = postObject.getString("title");
String content = postObject.getString("content");
String thumbnail = imagesPair.getString("url");
Log.d("Data", "Post id: " + id);
Log.d("Data", "Post title: " + title);
Log.d("Data", "Post title: " + thumbnail);
//Use the title and id as per your requirement
infoModels.add(new MainDataModel(id, title, content, thumbnail));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return ou_response;
}
#Override
protected void onPostExecute(String result) {
CustomProcessDialog.dissmis();
if (result != null) {
bus.post(infoModels);
}
}
}
}
When use this method LoadData(); in button action, not load new data just copy fist 10 post and load again this first 10 post!
How can i fix it and when click on Button click action, load new data ?
Attention : Please don't give me negative points, help me and i really need you helps! thanks all <3
I dont understand your code but in your AsyncTask this method should like that :
public void getMainDataInfo(Context context,int page) {
mContext = context;
new getInfo().execute(ServerAddress + "page="+page);
}
In your code you always getting page=1, there is no paging parameter in method
You can use SwipeRefreshLayout for this.