Trying create an EndlessScrollListener to ListView? - android

I'm trying create a ListView to paginate results of a webservice RestFull. The problem is with onLoadMore that execute always in onCreateView when I set setOnScrollListener and this always returns all data from webservice. My doubt is how could I execute this onLoadMore only when I need more data touching on ListView and not when it open in onCreateView.
I'm trying this.
EndlessScrollListener
public abstract class EndlessScrollListener implements AbsListView.OnScrollListener {
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 2;
// The current offset index of data you have loaded
private int currentPage = 0;
// The total number of items in the dataset after the last load
private int previousTotalItemCount = 0;
// True if we are still waiting for the last set of data to load.
private boolean loading = true;
// Sets the starting page index
private int startingPageIndex = 0;
public EndlessScrollListener() {
}
public EndlessScrollListener(int visibleThreshold) {
this.visibleThreshold = visibleThreshold;
}
public EndlessScrollListener(int visibleThreshold, int startPage) {
this.visibleThreshold = visibleThreshold;
this.startingPageIndex = startPage;
this.currentPage = startPage;
}
// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = totalItemCount;
if (totalItemCount == 0) { this.loading = true; }
}
// If it’s still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
if (loading && (totalItemCount > previousTotalItemCount)) {
loading = false;
previousTotalItemCount = totalItemCount;
currentPage++;
}
// If it isn’t currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
if (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) {
onLoadMore(currentPage + 1, totalItemCount);
loading = true;
}
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount);
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// Don't take any action on changed
}
}
Fragment
public class NoticiaFrag extends Fragment {
private ListView lvNoticias;
private List<Noticia> listaNoticias = new ArrayList<Noticia>();
private NoticiaListAdapter noticiaLA;
private static final String TAG = "NoticiaFrag";
protected ProgressDialog progressDialog;
//offset start in 0
private static Integer OFFSET = 0;
//singleton
private static NoticiaFrag mFrag;
public static NoticiaFrag newInstance() {
if(mFrag == null){
mFrag = new NoticiaFrag();
}
return mFrag;
}
public NoticiaFrag() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.noticia_frag, container, false);
lvNoticias = (ListView)view.findViewById(R.id.lvNoticias);
lvNoticias.setOnScrollListener(new EndlessScrollListener() {
#Override
public void onLoadMore(int page, int totalItemsCount) {
OFFSET += 2;
moreData();
}
});
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getAllNoticias();
}
/** retorna todas as noticias */
private void getAllNoticias(){
progressDialog = new CustomProgressDialog().getCustomProgress(null, getView().getContext());
progressDialog.show();
try {
JsonObjectRequest app = new NoticiaDAO().getAllNoticias(OFFSET, new NoticiasAdapter() {
#Override
public void getAllNoticias(List<Noticia> lista) {
Log.w("SIZELIST->", lista.size() + "") ;
if(!lista.isEmpty()){
listaNoticias = lista;
noticiaLA = new NoticiaListAdapter(getView().getContext(), listaNoticias);
lvNoticias.setAdapter(noticiaLA);
}else{
Toast.makeText(getView().getContext(), "Nenhuma noticia encontrada", Toast.LENGTH_SHORT).show();
}
progressDialog.dismiss();
}
});
CustomVolleySingleton.getInstance().addToRequestQueue(app);
}catch (Exception e ){
Log.e("ERROR: " + TAG, "Method: " + "getAllNoticias: " + e);
}
}
// Append more data into the adapter
public void moreData() {
progressDialog = new CustomProgressDialog().getCustomProgress(null, getView().getContext());
progressDialog.show();
try {
JsonObjectRequest app = new NoticiaDAO().getAllNoticias(OFFSET, new NoticiasAdapter() {
#Override
public void getAllNoticias(List<Noticia> lista) {
if(listaNoticias.size() > 0){
noticiaLA.changeLista(lista);
Log.w("MORE_DATA->", noticiaLA.getCount() + "");
}else{
Toast.makeText(getView().getContext(), "Nenhuma noticia encontrada", Toast.LENGTH_SHORT).show();
}
progressDialog.dismiss();
}
});
CustomVolleySingleton.getInstance().addToRequestQueue(app);
}catch (Exception e ){
Log.e("ERROR: " + TAG, "Method: " + "getAllNoticias: " + e);
}
}
#Override
public void onResume() {
super.onResume();
ChangeActionBar.changeActionBar(getActivity(), "Notícias", false, "");
}
#Override
public void onPause() {
super.onPause();
CustomVolleySingleton.getInstance().cancelPendingRequests(CustomVolleySingleton.TAG);
ChangeActionBar.changeActionBar(getActivity(), null, false, "");
OFFSET = 0;
}
}

Problem solved.
Fragment
public class NoticiaFrag extends Fragment {
private ListView lvNoticias;
private ProgressBar pbProgress;
private List<Noticia> listaNoticias = new ArrayList<Noticia>();
private NoticiaListAdapter noticiaLA;
private static final String TAG = "NoticiaFrag";
protected ProgressDialog progressDialog;
//offset start in 0
private static Integer OFFSET = 0;
//singleton
private static NoticiaFrag mFrag;
public static NoticiaFrag newInstance() {
if(mFrag == null){
mFrag = new NoticiaFrag();
}
return mFrag;
}
public NoticiaFrag() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.noticia_frag, container, false);
lvNoticias = (ListView)view.findViewById(R.id.lvNoticias);
pbProgress = (ProgressBar)view.findViewById(R.id.pbProgress);
lvNoticias.setOnScrollListener(new EndlessScrollListener() {
#Override
public void onLoadMore(int page, int totalItemsCount) {
moreData();
}
});
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getAllNoticias();
}
/** retorna todas as noticias */
private void getAllNoticias(){
pbProgress.setVisibility(View.VISIBLE);
try {
JsonObjectRequest app = new NoticiaDAO().getAllNoticias(OFFSET, new NoticiasAdapter() {
#Override
public void getAllNoticias(List<Noticia> lista) {
Log.w("SIZELIST->", lista.size() + "") ;
if(!lista.isEmpty()){
listaNoticias = lista;
noticiaLA = new NoticiaListAdapter(getView().getContext(), listaNoticias);
lvNoticias.setAdapter(noticiaLA);
}else{
Toast.makeText(getView().getContext(), "Nenhuma noticia encontrada", Toast.LENGTH_SHORT).show();
}
pbProgress.setVisibility(View.GONE);
}
});
CustomVolleySingleton.getInstance().addToRequestQueue(app);
}catch (Exception e ){
Log.e("ERROR: " + TAG, "Method: " + "getAllNoticias: " + e);
}
}
// Append more data into the adapter
public void moreData() {
OFFSET += 5;
Log.i("OFFSET", OFFSET + "");
pbProgress.setVisibility(View.VISIBLE);
try {
JsonObjectRequest app = new NoticiaDAO().getAllNoticias(OFFSET, new NoticiasAdapter() {
#Override
public void getAllNoticias(List<Noticia> lista) {
if(listaNoticias.size() > 0){
if(noticiaLA == null){
listaNoticias = lista;
noticiaLA = new NoticiaListAdapter(getView().getContext(), listaNoticias);
lvNoticias.setAdapter(noticiaLA);
}else{
noticiaLA.changeLista(lista);
}
Log.w("MORE_DATA->", noticiaLA.getCount() + "");
}else{
Toast.makeText(getView().getContext(), "Nenhuma noticia encontrada", Toast.LENGTH_SHORT).show();
}
pbProgress.setVisibility(View.GONE);
}
});
CustomVolleySingleton.getInstance().addToRequestQueue(app);
}catch (Exception e ){
Log.e("ERROR: " + TAG, "Method: " + "getAllNoticias: " + e);
}
}
#Override
public void onResume() {
super.onResume();
ChangeActionBar.changeActionBar(getActivity(), "Notícias", false, "");
}
#Override
public void onPause() {
super.onPause();
CustomVolleySingleton.getInstance().cancelPendingRequests(CustomVolleySingleton.TAG);
ChangeActionBar.changeActionBar(getActivity(), null, false, "");
OFFSET = 0;
}
}
Adapter
public class NoticiaListAdapter extends BaseAdapter {
private Context context;
private List<Noticia> lista;
public NoticiaListAdapter(Context context, List<Noticia> lista) {
this.context = context;
this.lista = lista;
}
public void changeLista(List<Noticia> lista){
this.lista.addAll(lista);
notifyDataSetChanged();
}
#Override
public int getCount() {
return lista.size();
}
#Override
public Object getItem(int position) {
return lista.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
Noticia noticia = lista.get(position);
if (convertView == null) {
holder = new ViewHolder();
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.noticia_listadapter, parent, false);
holder.llNoticiaListAdapter = (LinearLayout) convertView.findViewById(R.id.llNoticiaListAdapter);
holder.sivNoticia = (SmartImageView)convertView.findViewById(R.id.sivNoticia);
holder.tvTitulo = (TextView) convertView.findViewById(R.id.tvTitulo);
holder.wvDescricao = (WebView) convertView.findViewById(R.id.wvDescricao);
holder.tvData = (TextView) convertView.findViewById(R.id.tvData);
holder.tvAutor = (TextView) convertView.findViewById(R.id.tvAutor);
convertView.setTag(holder);
}else{
holder = (ViewHolder)convertView.getTag();
}
if(noticia.getImage() == null){
holder.sivNoticia.setImageDrawable(context.getResources().getDrawable(R.drawable.no_image_news));
}else{
holder.sivNoticia.setImageUrl(WebServiceURL.getBaseWebServiceURL() + "app/webroot/img/noticias/" + noticia.getImage());
}
holder.tvTitulo.setText(noticia.getTitulo());
holder.wvDescricao.loadData(formatDescricao(noticia.getDescricao()), "text/html; charset=utf-8", null);
holder.tvData.setText(new DateControl().getDataHoraFormat(noticia.getCreated()));
holder.tvAutor.setText("Por: " + noticia.getUsuario());
return convertView;
}
private String formatDescricao(String descricao){
StringBuilder html = new StringBuilder();
html.append("<html>");
html.append("<body>");
html.append("<small>");
html.append("<p align=\"justify\" style=\"color:gray\">");
html.append(descricao);
html.append("</p");
html.append("</small>");
html.append("</body>");
html.append("</html>");
return html.toString();
}
private static class ViewHolder{
LinearLayout llNoticiaListAdapter;
SmartImageView sivNoticia;
TextView tvTitulo;
WebView wvDescricao;
TextView tvData;
TextView tvAutor;
}
}
WebService CakePHP
public function getAllNoticias(){
$json = $this->request->input("json_decode", true);
//$limit = $json["limit"];
$offset = $json["offset"];
$noticias = $this->Noticia->find("all", array("fields"=> array(
"Noticia.id",
"Noticia.titulo",
"Noticia.descricao",
"Noticia.usuario",
"Noticia.image",
"Noticia.created"
),
"order"=>"Noticia.created desc",
"offset"=>$offset,
"limit"=>5,
));
$this->set("noticias", $noticias);
$this->set("_serialize", array("result"=>"noticias"));
}

Related

Increment Page Index for Paginated Recyclerview

I have implemented pagination in my app but I am having trouble with incrementing the page index. I am getting data from unsplash api and have formed the request url like below:
private String url = Constants.SEARCH_URL + "top30&page="+ PAGE_INDEX;
and this is the url used below:
https://api.unsplash.com/search/photos?client_id=api_key&order_by=latest&query=top30&page=1
In my fragment, I have a constant int like private static int PAGE_INDEX = 1; which tracks the current page and used for getting the next pages till page limit.
The first request runs fine and when scrolling towards the last item, more items are fetched. However, these items are same as from the first page index and after logging the url, I get the same as in first request like below:
https://api.unsplash.com/search/photos?client_id=api_key&order_by=latest&query=top30&page=1
I have attached my scroll listener in recyclerview like this and incremented the PAGE_INDEX but not getting the next page in url but in log cat it shows the page index as 2. Here's the listener attached:
top30RV.addOnScrollListener(new PaginationScrollListener(llm) {
#Override
protected void loadMoreItems() {
isLoading = true;
PAGE_INDEX = PAGE_INDEX + 1;
Log.d(TAG, "PIn:\t" + PAGE_INDEX);
loadNextPage();
}
#Override
protected int getTotalPageCount() {
return PAGE_COUNT;
}
#Override
public boolean isLoading() {
return isLoading;
}
#Override
public boolean isLastPage() {
return isLastPage;
}
});
and my PaginationScrollListener class code:
public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
LinearLayoutManager llm;
public PaginationScrollListener(LinearLayoutManager llm) {
this.llm = llm;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visisbleItemCount = recyclerView.getChildCount();
int totalItemCount = llm.getItemCount();
int firstVisibleItemPosn = llm.findFirstVisibleItemPosition();
if (!isLoading() && !isLastPage()){
if ((visisbleItemCount + firstVisibleItemPosn) >= totalItemCount && firstVisibleItemPosn > 0){
loadMoreItems();
}
}
}
protected abstract void loadMoreItems();
protected abstract int getTotalPageCount();
public abstract boolean isLoading();
public abstract boolean isLastPage();
}
in my adapter I have called the method to addLoadingFooter and it works but also the progressbar is not dismissed and removeLoadingFooter is called but doesn't work because of the page index is less than the page limit.
public class Top30Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = Top30Adapter.class.getSimpleName();
private final Context context;
private List<UnsplashImages> itemsList;
private UnsplashImages unsplashImages;
private static final int ITEMS = 0;
private static final int PROGRESS = 1;
private boolean isLoadingAdded = false;
private boolean retryPageLoad = false;
private String errorMsg;
private PaginationAdapterCallback mCallback;
public Top30Adapter(Context context, List<UnsplashImages> itemsList) {
this.context = context;
this.itemsList = itemsList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType){
case ITEMS:
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.wallpaper_items_layout, parent, false);
return new WallpaperItemViewHolder(view);
case PROGRESS:
View pv = LayoutInflater.from(parent.getContext()).inflate(R.layout.progress_items_layout, parent, false);
return new LoadingViewHolder(pv);
}
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewholder, final int position) {
switch (getItemViewType(position)){
case ITEMS:
WallpaperItemViewHolder wallpaperItemViewHolder = (WallpaperItemViewHolder) viewholder;
unsplashImages = (UnsplashImages) itemsList.get(position);
Picasso.with(context)
.load(unsplashImages.getRegularImg())
.placeholder(R.drawable.drawer_header_trimmed)
.into(wallpaperItemViewHolder.wallpaperItemImg);
wallpaperItemViewHolder.favoriteWP_IV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Favorites favorites = new Favorites();
favorites.setFavoritesId(UUID.randomUUID().toString());
favorites.setLargeImgURL(unsplashImages.getRegularImg());
favorites.setPreviewImgURL(unsplashImages.getRegularImg());
favorites.save();
Toast.makeText(context, "Added to Favorites", Toast.LENGTH_SHORT).show();
}
});
wallpaperItemViewHolder.setWallPaper_TV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final WallpaperManager wpm = WallpaperManager.getInstance(context);
Picasso.with(context)
.load(((UnsplashImages) itemsList.get(position)).getRegularImg())
.into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
try {
wpm.setBitmap(bitmap);
Toast.makeText(context, "Your New Wallpaper Has Been Set", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
Log.d(TAG, "Bitmap Load Failed");
Toast.makeText(context, "Could Not Set Wallpaper...Choose Another", Toast.LENGTH_SHORT).show();
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
Log.d(TAG, "Prep to Load Bitmap");
}
});
}
});
break;
case PROGRESS:
LoadingViewHolder loadingViewHolder = (LoadingViewHolder) viewholder;
if (retryPageLoad){
loadingViewHolder.mErrorLayout.setVisibility(View.VISIBLE);
loadingViewHolder.mProgressBar.setVisibility(View.GONE);
loadingViewHolder.mErrorTxt.setText(errorMsg != null
? errorMsg
: context.getString(R.string.error_msg_unknown));
} else {
loadingViewHolder.mErrorLayout.setVisibility(View.GONE);
loadingViewHolder.mProgressBar.setVisibility(View.VISIBLE);
}
break;
}
}
#Override
public int getItemCount() {
if (itemsList == null) {
return 0;
}
return itemsList.size();
}
#Override
public int getItemViewType(int position) {
return (position == itemsList.size() - 1 && isLoadingAdded) ? PROGRESS : ITEMS;
}
public void addImage(UnsplashImages images){
itemsList.add(images);
notifyItemInserted(itemsList.size() - 1);
}
public void addAllImages(List<UnsplashImages> list){
for (UnsplashImages images : list){
addImage(images);
}
}
public void addLoadingFooter() {
isLoadingAdded = true;
addImage(new UnsplashImages());
}
public void removeLoadingFooter(){
isLoadingAdded = false;
int position = itemsList.size() - 1;
UnsplashImages unsplashImages = getItem(position);
if (unsplashImages != null){
itemsList.remove(unsplashImages);
notifyItemRemoved(position);
}
}
private UnsplashImages getItem(int position) {
return itemsList.get(position);
}
public void showRetry(boolean show, #Nullable String errorMsg){
retryPageLoad = true;
notifyItemChanged(itemsList.size() - 1);
if (errorMsg != null){this.errorMsg = errorMsg; } else { errorMsg = "Can't Fetch Wallpapers Now..."; }
}
}
Full fragment code:
public class Top30Fragment extends Fragment {
private static final String TAG = Top30Fragment.class.getSimpleName();
private RecyclerView top30RV;
private List<UnsplashImages> itemsList = new ArrayList<>();
private Top30Adapter adapter;
private int PAGE_COUNT;
private static int PAGE_INDEX = 1;
private static int PER_PAGE = 20;
private static int CURRENT_PAGE = PAGE_INDEX;
private boolean isLoading = false;
private boolean isLastPage = false;
private String url = Constants.SEARCH_URL + "top30&page="+ PAGE_INDEX;
public Top30Fragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View vtop = inflater.inflate(R.layout.fragment_top30, container, false);
top30RV = vtop.findViewById(R.id.top30RV);
top30RV.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
top30RV.setLayoutManager(llm);
if (Utils.isNetwork(getActivity())){
fetchWallpapers();
} else {
Snackbar.make(getActivity().findViewById(android.R.id.content), "Check Connection", Snackbar.LENGTH_LONG).show();
}
adapter = new Top30Adapter(getActivity(), itemsList);
top30RV.setAdapter(adapter);
top30RV.addOnScrollListener(new PaginationScrollListener(llm) {
#Override
protected void loadMoreItems() {
isLoading = true;
PAGE_INDEX = PAGE_INDEX + 1;
Log.d(TAG, "PIn:\t" + PAGE_INDEX);
loadNextPage();
}
#Override
protected int getTotalPageCount() {
return PAGE_COUNT;
}
#Override
public boolean isLoading() {
return isLoading;
}
#Override
public boolean isLastPage() {
return isLastPage;
}
});
adapter.notifyDataSetChanged();
return vtop;
}
private void loadNextPage() {
int newIndex = PAGE_INDEX +=1;
Log.d(TAG, "Next Page:\t" + newIndex);
Log.d(TAG, "Next Page Index:\t" + newIndex);
Log.d(TAG, "Next Page URL:\t" + url);
AndroidNetworking.get(url)
.setTag("Load Next Top30")
.setPriority(Priority.HIGH)
.build()
.getAsJSONObject(new JSONObjectRequestListener() {
#Override
public void onResponse(JSONObject response) {
Log.d(TAG, "Next Page Response:\t" + response.toString());
if (response != null){
isLoading = false;
adapter.removeLoadingFooter();
try {
JSONObject jsonObject = new JSONObject(response.toString());
JSONArray results = jsonObject.getJSONArray("results");
for (int p = 0; p < results.length(); p++){
JSONObject items = results.getJSONObject(p);
UnsplashImages images = new UnsplashImages();
images.setImageId(items.getString("id"));
JSONObject urls = items.getJSONObject("urls");
images.setRawImg(urls.getString("raw"));
images.setFullImg(urls.getString("full"));
images.setRegularImg(urls.getString("regular"));
images.setSmallImg(urls.getString("small"));
images.setThumbImg(urls.getString("thumb"));
itemsList.add(images);
adapter.notifyDataSetChanged();
Log.d(TAG, "New List Size:\t" + itemsList.size());
if (CURRENT_PAGE != PAGE_COUNT){
adapter.addLoadingFooter();
} else {
isLastPage = true;
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
#Override
public void onError(ANError anError) {
}
});
}
private void fetchWallpapers() {
// String url = Constants.SEARCH_URL + "top30&page="+ PAGE_INDEX + "per_page=" + PER_PAGE;
Log.d(TAG, "Top30 Unsplash URL:\t" + url);
AndroidNetworking.get(url)
.setPriority(Priority.HIGH)
.setTag("Get Seasons Wallpapers")
.build()
.getAsJSONObject(new JSONObjectRequestListener() {
#Override
public void onResponse(JSONObject response) {
Log.d(TAG, "Un-Response:\t" + response.toString());
if (response != null){
try {
JSONObject jsonObject = new JSONObject(response.toString());
PAGE_COUNT = jsonObject.getInt("total_pages");
Log.d(TAG, "Page Count:\t" + PAGE_COUNT);
JSONArray results = jsonObject.getJSONArray("results");
for (int p = 0; p < results.length(); p++){
JSONObject items = results.getJSONObject(p);
UnsplashImages images = new UnsplashImages();
images.setImageId(items.getString("id"));
JSONObject urls = items.getJSONObject("urls");
images.setRawImg(urls.getString("raw"));
images.setFullImg(urls.getString("full"));
images.setRegularImg(urls.getString("regular"));
images.setSmallImg(urls.getString("small"));
images.setThumbImg(urls.getString("thumb"));
itemsList.add(images);
adapter.notifyDataSetChanged();
Log.d(TAG, "List Size:\t" + itemsList.size());
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
#Override
public void onError(ANError anError) {
}
});
}
Is there another way to construct the request url to increment the page index? Thanks.

Deleting all items in a page one by one in Android recyclerview does not load the next set of data

I have implemented recyclerview and it loads 10 tasks at a time. Once every task is completed, I am removing the task with a completed button. It loads the 11th item if I scroll to the bottom at one condition there has to be atleast one uncompleted task present on the page. If I clear all 10 items it just shows me loading animation and nothing happens. I have to close the app and open again to load the tasks again.
I have implemented a PaginationActivity, PaginationAdapter and scroll listener for the page. It loads data from a local database whenever you have reached the end of the page and you have got more tasks to load.
PaginationActivity.java
public class PaginationActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
PaginationAdapter adapter;
LinearLayoutManager linearLayoutManager;
public static ArrayList<String> reasonsdata = new ArrayList<String>();
public static String rslt="";
public static String[] reasons;
public static boolean chk = true;
public static String payTypeSelected;
public static String reasonSelected;
public static HashMap<String, EditText> drivernotesMap = new HashMap<String, EditText>();
public static HashMap<String, EditText> paynoteMap = new HashMap<String, EditText>();
public static HashMap<String, String> paytypeMap = new HashMap<String, String>();
RecyclerView rv;
ProgressBar progressBar;
private static final int PAGE_START = 0;
private boolean isLoading = false;
private boolean isLastPage = false;
private int TOTAL_PAGES;
private int currentPage = PAGE_START;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pagination_main);
fillspinner();
rv = (RecyclerView) findViewById(R.id.main_recycler);
progressBar = (ProgressBar) findViewById(R.id.main_progress);
adapter = new PaginationAdapter(this);
linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
rv.setLayoutManager(linearLayoutManager);
rv.setItemAnimator(new DefaultItemAnimator());
rv.setAdapter(adapter);
rv.addOnScrollListener(new PaginationScrollListener(linearLayoutManager) {
#Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1;
// mocking network delay for API call
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadNextPage();
}
}, 1000);
}
#Override
public int getTotalPageCount() {
return TOTAL_PAGES;
}
#Override
public boolean isLastPage() {
return isLastPage;
}
#Override
public boolean isLoading() {
return isLoading;
}
});
// mocking network delay for API call
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadFirstPage();
}
}, 1000);
}
private void fillspinner() {
reasonsdata.clear();
try
{
rslt="START";
CallReasons cr = new CallReasons();
cr.join();
cr.start();
while(rslt=="START") {
try {
Thread.sleep(10);
}catch(Exception ex) {
}
}
}
catch (Exception ex) {
ex.printStackTrace();
}
if(rslt == "Success"){
reasonsdata.add("<--- Select Reason --->");
for(int i =0; i< reasons.length;i++){
reasonsdata.add(reasons[i]);
}
}
}
public void loadFirstPage() {
Log.d(TAG, "loadFirstPage: ");
TOTAL_PAGES = Run.getTotalPages();
//List<Run> runs = Run.createRuns(adapter.getItemCount());
List<Run> runs = Run.createRuns(adapter.getMaxID());
progressBar.setVisibility(View.GONE);
adapter.addAll(runs);
if (currentPage <= TOTAL_PAGES) adapter.addLoadingFooter();
else isLastPage = true;
}
public void loadNextPage() {
Log.d(TAG, "loadNextPage: " + currentPage);
//List<Run> runs = Run.createRuns(adapter.getItemCount());
List<Run> runs = Run.createRuns(adapter.getMaxID());
adapter.removeLoadingFooter();
isLoading = false;
adapter.addAll(runs);
if (currentPage != TOTAL_PAGES) adapter.addLoadingFooter();
else isLastPage = true;
}
}
PaginationAdapter.java
public class PaginationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int ITEM = 0;
private static final int LOADING = 1;
private List<Run> runs = new ArrayList<>();
private Context context;
private boolean isLoadingAdded = false;
private String cash = "CASH";
private String cheque = "CHEQUE";
SQLiteDatabase db;
//PaginationActivity func_call;
public PaginationAdapter(Context context) {
this.context = context;
runs = new ArrayList<>();
//func_call = new PaginationActivity();
}
public List<Run> getRuns() {
return runs;
}
public void setRuns(List<Run> runs) {
this.runs = runs;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case ITEM:
viewHolder = getViewHolder(parent, inflater);
break;
case LOADING:
View v2 = inflater.inflate(R.layout.item_progress, parent, false);
viewHolder = new LoadingVH(v2);
//viewHolder = getViewHolder(parent, inflater);
//func_call.loadNextPage();
break;
}
return viewHolder;
}
#NonNull
private RecyclerView.ViewHolder getViewHolder(ViewGroup parent, LayoutInflater inflater) {
RecyclerView.ViewHolder viewHolder;
View v1 = inflater.inflate(R.layout.activity_main, parent, false);
viewHolder = new RunVH(v1);
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final Run run = runs.get(position);
switch (getItemViewType(position)) {
case ITEM:
final RunVH runVH = (RunVH) holder;
//adding text areas to show data
//Completed task button
runVH.btnSave.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(context, "Mark as Completed", Toast.LENGTH_LONG).show();
remove(run);
}
}
} catch (Exception ex) {
//pbbar.setVisibility(View.GONE);
ex.printStackTrace();
}
}
});
break;
case LOADING:
break;
}
}
#Override
public int getItemCount() {
return runs == null ? 0 : runs.size();
}
//#Override
public int getMaxID() {
if(runs == null || runs.size() == 0){
return 0;
}else{
Run test = runs.get(runs.size()-2);
int maxid = (int) test.getProperty(0);
return maxid;
}
}
#Override
public int getItemViewType(int position) {
return (position == runs.size() - 1 && isLoadingAdded) ? LOADING : ITEM;
}
public void add(Run run) {
runs.add(run);
notifyItemInserted(runs.size() - 1);
}
public void addAll(List<Run> runList) {
for (Run run : runList) {
add(run);
}
}
public void remove(Run run) {
int position = runs.indexOf(run);
if (position > -1) {
runs.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, runs.size());
}
/*if(runs.size() < 0){
clear();
func_call.loadNextPage();
}*/
}
public void clear() {
isLoadingAdded = false;
while (getItemCount() > 0) {
remove(getItem(0));
}
}
public boolean isEmpty() {
return getItemCount() == 0;
}
public void addLoadingFooter() {
isLoadingAdded = true;
add(new Run());
}
public void removeLoadingFooter() {
isLoadingAdded = false;
int position = runs.size() - 1;
Run item = getItem(position);
if (item != null) {
runs.remove(position);
notifyItemRemoved(position);
}
}
public Run getItem(int position) {
return runs.get(position);
}
protected class RunVH extends RecyclerView.ViewHolder {
private TextView textEsky;
private TextView textAdjusted;
private TextView textAddress;
private TextView textNew;
private TextView textTrusted;
private TextView textName;
private TextView textMobile;
private TextView textHome;
private TextView textWork;
private TextView payType;
private EditText textPayNote;
private EditText textDeliveryInst;
private EditText textDriverNotes;
private final Spinner reasons;
private RadioGroup rg;
private Button btnSave;
private CheckBox IsDriverData;
public RunVH(View itemView) {
super(itemView);
textEsky = (TextView) itemView.findViewById(R.id.idEsky);
textAdjusted = (TextView) itemView.findViewById(R.id.idAdjusted);
textAddress = (TextView) itemView.findViewById(R.id.idAddress);
textNew = (TextView) itemView.findViewById(R.id.idNew);
textTrusted = (TextView) itemView.findViewById(R.id.idTrusted);
textName = (TextView) itemView.findViewById(R.id.idName);
textMobile = (TextView) itemView.findViewById(R.id.idMobile);
textHome = (TextView) itemView.findViewById(R.id.idHome);
textWork = (TextView) itemView.findViewById(R.id.idWork);
payType = (TextView) itemView.findViewById(R.id.idPayType);
textPayNote = (EditText) itemView.findViewById(R.id.idPayNoteText);
textDeliveryInst = (EditText) itemView.findViewById(R.id.idDeliInsText);
textDriverNotes = (EditText) itemView.findViewById(R.id.idDriverNotesText);
IsDriverData = (CheckBox) itemView.findViewById(R.id.idCheckBox);
reasons = (Spinner) itemView.findViewById(R.id.idReason);
rg = (RadioGroup) itemView.findViewById(R.id.Radiogroup);
btnSave = (Button) itemView.findViewById(R.id.button);
}
}
protected class LoadingVH extends RecyclerView.ViewHolder {
public LoadingVH(View itemView) {
super(itemView);
}
}
}
PaginationScrollListener.java
public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
LinearLayoutManager layoutManager;
public PaginationScrollListener(LinearLayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
if (!isLoading() && !isLastPage()) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
&& firstVisibleItemPosition >= 0) {
loadMoreItems();
}
}
}
protected abstract void loadMoreItems();
public abstract int getTotalPageCount();
public abstract boolean isLastPage();
public abstract boolean isLoading();
}
I could be wrong, but it looks to me like the only time you call loadNextPage() is when the RecyclerView gets a scroll event. Removing ViewHolders doesn't cause scroll events iirc.
If you register an AdapterDataObserver on the PaginationAdapter, you can listen in on removal/add events and trigger loads when there aren't any tasks left. You could probably do the same thing with some small changes to that commented-out code in remove(Run):
if (runs.isEmpty()) {
func_call.loadNextPage();
}
EDIT:
Btw, you see where you called adapter = new PaginationAdapter(this); in the PaginationActivity.onCreate method? The this here is your PaginationActivity, which means you can assign it to a PaginationAdapter field like so:
/**
* #param context the activity this adapter will appear in
*/
public PaginationAdapter(Context context) {
this.context = context;
runs = new ArrayList<>();
func_call = (PaginationActivity)context;
}
That should be enough to prevent crashes. For better separation of concerns, you'll probably want to switch over to AdapterDataObserver-based logic (I think this is called the open/closed principle? Maybe?).

Duplicate value append in recycleview multiple view android pagination

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();

fetch only 20 rows from server with setOnScrollListener

Its the first time I am using onscroll listener. My problem is how to fetch 20 new rows every time I scroll down ?.
I understand when I scroll down the grid view this code will be executed.
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
this.currentFirstVisibleItem = firstVisibleItem;
this.currentVisibleItemCount = visibleItemCount;
this.totalItem = totalItemCount;
if ((totalItemCount - visibleItemCount) <= (firstVisibleItem + 20)) {
// the below when executed will get all the rows ,I only need 20 rows
handler.execute().get();
}
}
this is my code
// the load more in the grid view, fetch new images.
mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
private int currentVisibleItemCount;
private int currentScrollState;
private int currentFirstVisibleItem;
private int totalItem;
private LinearLayout lBelow;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
this.currentScrollState = scrollState;
this.isScrollCompleted();
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
this.currentFirstVisibleItem = firstVisibleItem;
this.currentVisibleItemCount = visibleItemCount;
this.totalItem = totalItemCount;
if ((totalItemCount - visibleItemCount) <= (firstVisibleItem + 20)) {
// load new 20 row but how to do that
}
}
private void isScrollCompleted() {
if (totalItem - currentFirstVisibleItem == currentVisibleItemCount
&& this.currentScrollState == SCROLL_STATE_IDLE) {
Log.d("a", "poooppii");
}
}
;
});
this is where the connection happens
protected void showList(){
try {
JSONObject jsonObj = new JSONObject(myJSON);
peoples = jsonObj.getJSONArray("result");
System.out.println("Length:"+peoples.length());
int J_length=peoples.length()-1;
jsonObj= peoples.getJSONObject(J_length);
Listitem = new ArrayList<Listitem>();
for (int i = 0; i < peoples.length(); i++) {
JSONObject c = peoples.getJSONObject(i);
String id = c.getString("id");
String url = c.getString("url");
int intid = 0;
try {
intid = Integer.parseInt(id.toString());
} catch (NumberFormatException nfe) {
System.out.println("Could not parse " + nfe);
}
Listitem.add(new Listitem(id, url));
System.out.println(Listitem);
}
//}
if (mListener != null)
mListener.myMethod(Listitem);
} catch (JSONException e) {
e.printStackTrace();
}
}
You need to maintain base and limit for data load. Initialize your base and limit variable.
int base = 0, limit = 20;
Increase your base variable whenever loading new items.
base = base + limit;
handler.execute().get();
Pass base and limit variable in your API call, so that you can use it in query from API side.
Note : base is a value from which position you need to start fetching values. limit is a value for how many rows you need to fetch.
Refer this for limit query in sqlite.
Refer this for limit query in MySql.
Refer this for limit query in SQL.
Here is an working code, Kindly modify as per your use -
Let assume following is your model -
public class MyModel {
String name;
}
You need one interface -
public interface PagingListener<T> {
void onItemUpdate(ArrayList<T> allData);
}
Design your adapter like this -
public class CouponsAdapter extends RecyclerView.Adapter<CouponsAdapter.CouponHolder> implements View.OnClickListener, PagingListener<MyModel> {
private final static int TYPE_LOADING = 0;
private final static int TYPE_DATA = 1;
private FragmentActivity activity;
private LayoutInflater inflater;
private PagingRequestHandler<MyModel> pagingRequestHandler;
private ArrayList<MyModel> data = null;
public CouponsAdapter(FragmentActivity activity) {
this.activity = activity;
inflater = LayoutInflater.from(activity);
this.data = new ArrayList<>();
this.pagingRequestHandler = new PagingRequestHandler<>(data, this);
}
#Override
public int getItemViewType(int position) {
return position >= data.size() ? TYPE_LOADING : TYPE_DATA;
}
#Override
public CouponHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == TYPE_DATA) {
view = inflater.inflate(R.layout.item_coupons_card_block, parent, false);
} else {
view = inflater.inflate(R.layout.item_drawer_payment_loading, parent, false);
}
return new CouponHolder(view, viewType);
}
#Override
public void onBindViewHolder(CouponHolder holder, int position) {
if (getItemViewType(position) == TYPE_DATA) {
//Bind your view here
}
pagingRequestHandler.checkAndMakeRequestForMoreData(position); //Will check and make request
}
#Override
public int getItemCount() {
return data.size() + (pagingRequestHandler.isNoMoreDataLeft() ? 0 : 1); // If no data then it will return 1 to show loading
}
#Override
public void onClick(View v) {
}
#Override
public void onItemUpdate(ArrayList<MyModel> allData) {
this.data = allData; // will update data with updated content
notifyDataSetChanged();
}
public class CouponHolder extends RecyclerView.ViewHolder {
public CouponHolder(View itemView, int itemType) {
super(itemView);
if (itemType == TYPE_DATA) {
}
}
}
}
And now this is the paging request handler, that will keep track on page number and will make request for next page -
public class PagingRequestHandler<T> {
public static final int PAGE_SIZE = 10;
private final PagingListener listener;
private final ArrayList<MyModel> arrayList;
private final String url = "Your Url here";
private boolean noMoreDataLeft;
private int pagingIndex = 0;
private boolean requestGoingOn;
public PagingRequestHandler(ArrayList<MyModel> data, PagingListener<MyModel> listener) {
this.listener = listener;
this.arrayList = data;
fetchMoreData();
}
private void fetchMoreData() {
requestGoingOn = true;
Call<MyModel[]> getCoupons = Utils.getRetrofit().getCoupons(url + "/" + pagingIndex); // I am using retrofit for network call, you can use your own
getCoupons.enqueue(new Callback<MyModel[]>() {
#Override
public void onResponse(Response<MyModel[]> response, Retrofit retrofit) {
try {
requestGoingOn = false;
MyModel[] couponModelDses = response.body();
if (couponModelDses != null) {
for (MyModel couponModelDse : couponModelDses) {
arrayList.add(couponModelDse);
}
if (couponModelDses.length < PAGE_SIZE)
noMoreDataLeft = true;
listener.onItemUpdate(arrayList);
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable t) {
requestGoingOn = false;
}
});
}
public boolean isNoMoreDataLeft() {
return noMoreDataLeft;
}
public void checkAndMakeRequestForMoreData(int position) {
if (!noMoreDataLeft && !requestGoingOn && position > (PAGE_SIZE * pagingIndex)) {
if ((position % PAGE_SIZE) > (int) (PAGE_SIZE * .7)) {
pagingIndex++;
fetchMoreData();
}
}
}
}
How it works - The adapter is initialising the Handler, which make network call for first time. The adapter now calls handler api to check if 70% of page is scrolled on every onBind. Now if 70% of data has been shown then it makes network call againe and makes a variable called requestGoingOn true, so you can show a loader in list. Wher request completed the handler is adding data to the arraylist and updating adapter by calling the callback onItemUpdate.
Hope it will help, please let me know if you need further assistance :)

Volley - How to Cache loaded data to disk and load them when no connection available or at start of the application

I want to use Volley to build my rest client. I found some good example (posted below) that do exactly what I want except the caching. I want to cache the results to the sdcard and load the cached result on starting the app. Any clue for how to modify the code below to enable caching for text and images of the list view items ?
/**
* Demonstrates: 1. ListView which is populated by HTTP paginated requests; 2. Usage of NetworkImageView;
* 3. "Endless" ListView pagination with read-ahead
*
* Please note that for production environment you will need to add functionality like handling rotation,
* showing/hiding (indeterminate) progress indicator while loading, indicating that there are no more records, etc...
*
* #author Ognyan Bankov (ognyan.bankov#bulpros.com)
*
*/
public class Act_NetworkListView extends Activity {
private static final int RESULTS_PAGE_SIZE = 20;
private ListView mLvPicasa;
private boolean mHasData = false;
private boolean mInError = false;
private ArrayList<PicasaEntry> mEntries = new ArrayList<PicasaEntry>();
private PicasaArrayAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act__network_list_view);
mLvPicasa = (ListView) findViewById(R.id.lv_picasa);
mAdapter = new PicasaArrayAdapter(this, 0, mEntries, new ImageLoader(queue, new BitmapCache(4)));
mLvPicasa.setAdapter(mAdapter);
mLvPicasa.setOnScrollListener(new EndlessScrollListener());
}
#Override
protected void onResume() {
super.onResume();
if (!mHasData && !mInError) {
loadPage();
}
}
RequestQueue queue;
private void loadPage() {
queue = MyVolley.getRequestQueue();
int startIndex = 1 + mEntries.size();
JsonObjectRequest myReq = new JsonObjectRequest(Method.GET,
"https://picasaweb.google.com/data/feed/api/all?q=kitten&max-results="
+
RESULTS_PAGE_SIZE
+
"&thumbsize=160&alt=json"
+ "&start-index="
+ startIndex,
null,
createMyReqSuccessListener(),
createMyReqErrorListener());
if (myReq.getCacheEntry().data != null) {
}
queue.add(myReq);
}
private Response.Listener<JSONObject> createMyReqSuccessListener() {
return new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONObject feed = response.getJSONObject("feed");
JSONArray entries = feed.getJSONArray("entry");
JSONObject entry;
for (int i = 0; i < entries.length(); i++) {
entry = entries.getJSONObject(i);
String url = null;
JSONObject media = entry.getJSONObject("media$group");
if (media != null && media.has("media$thumbnail")) {
JSONArray thumbs = media.getJSONArray("media$thumbnail");
if (thumbs != null && thumbs.length() > 0) {
url = thumbs.getJSONObject(0).getString("url");
}
}
mEntries.add(new PicasaEntry(entry.getJSONObject("title").getString("$t"), url));
}
mAdapter.notifyDataSetChanged();
} catch (JSONException e) {
showErrorDialog();
}
}
};
}
private Response.ErrorListener createMyReqErrorListener() {
return new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
showErrorDialog();
}
};
}
private void showErrorDialog() {
mInError = true;
AlertDialog.Builder b = new AlertDialog.Builder(Act_NetworkListView.this);
b.setMessage("Error occured");
b.show();
}
/**
* Detects when user is close to the end of the current page and starts loading the next page
* so the user will not have to wait (that much) for the next entries.
*
* #author Ognyan Bankov (ognyan.bankov#bulpros.com)
*/
public class EndlessScrollListener implements OnScrollListener {
// how many entries earlier to start loading next page
private int visibleThreshold = 5;
private int currentPage = 0;
private int previousTotal = 0;
private boolean loading = true;
public EndlessScrollListener() {
}
public EndlessScrollListener(int visibleThreshold) {
this.visibleThreshold = visibleThreshold;
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
currentPage++;
}
}
if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
// I load the next page of gigs using a background task,
// but you can call any function here.
loadPage();
loading = true;
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
public int getCurrentPage() {
return currentPage;
}
}
The Adapter:
public class PicasaArrayAdapter extends ArrayAdapter<PicasaEntry> {
private ImageLoader mImageLoader;
public PicasaArrayAdapter(Context context,
int textViewResourceId,
List<PicasaEntry> objects,
ImageLoader imageLoader
) {
super(context, textViewResourceId, objects);
mImageLoader = imageLoader;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.lv_picasa_row, null);
}
ViewHolder holder = (ViewHolder) v.getTag(R.id.id_holder);
if (holder == null) {
holder = new ViewHolder(v);
v.setTag(R.id.id_holder, holder);
}
PicasaEntry entry = getItem(position);
if (entry.getThumbnailUrl() != null) {
holder.image.setImageUrl(entry.getThumbnailUrl(), mImageLoader);
} else {
holder.image.setImageResource(R.drawable.no_image);
}
holder.title.setText(entry.getTitle());
return v;
}
private class ViewHolder {
NetworkImageView image;
TextView title;
public ViewHolder(View v) {
image = (NetworkImageView) v.findViewById(R.id.iv_thumb);
title = (TextView) v.findViewById(R.id.tv_title);
v.setTag(this);
}
}
}
This is pretty easy with volley as caching is build in. All you need is to call getCache with url of the request. Of course keep in mind that server is responsible to set max age of the cache and if time was passed cache is cleared
For example try this:
if(queue.getCache().get(url)!=null){
//response exists
String cachedResponse = new String(queue.getCache().get(url).data);
}else{
//no response
queue.add(stringRequest);
}
For caching is great to use VolleyExtended: eu.the4thfloor.volleyextended There is updated Response listener, where you get additional boolean changed which means if response is different to last response in cache. And on error response there is additional parameter response (cached) which you can use in case of server or network outtage. Of course if it is cached only.
private class ResponseListenerYYY extends Api.ResponseListener {
#Override
public void onResponse(String response, boolean changed) {
}
#Override
public void onErrorResponse(VolleyError error, String response) {
}
}

Categories

Resources