I face a difficulty when working using recyclerview and Gridview. The problem is when my application success load next page (next data), the recyclerview always back to the top.
I want the recyclerview start from last index.
Example : First page load 10 items, after success loan next page, the recycler view start scrolling from item 8.
For resolve that problem, i have tried all the solution on stackoverflow still get nothing.
Here are my code :
package com.putuguna.sitehinduapk.adapters;
import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.putuguna.sitehinduapk.R;
import com.putuguna.sitehinduapk.activities.DetailBlogPostActivity;
import com.putuguna.sitehinduapk.models.listposts.ItemPostModel;
import com.putuguna.sitehinduapk.utils.GlobalFunction;
import com.putuguna.sitehinduapk.utils.GlobalVariable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class ListPostAdapter extends RecyclerView.Adapter<ListPostAdapter.ViewHolder>{
private List<ItemPostModel> mListPost;
private Context mContext;
public ListPostAdapter(List<ItemPostModel> mListPost, Context mContext) {
this.mListPost = mListPost;
this.mContext = mContext;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.adapter_item_list_post, null);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
final ItemPostModel post = mListPost.get(position);
List<String> listURL = extractUrls(post.getContentPosting());
Glide.with(mContext)
.load(listURL.get(0))
.into(holder.ivImagePost);
holder.tvTitle.setText(post.getTitle());
holder.llItemPost.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//TO DO OnClick
}
});
List<String> listLabel = post.getListLabel();
String labelPost="";
for(int i=0; i<listLabel.size();i++){
labelPost += "#"+listLabel.get(i) + " ";
}
holder.tvLabel.setText(labelPost);
if ((position >= getItemCount() - 1))
load();
}
public abstract void load();
#Override
public int getItemCount() {
return mListPost.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder{
public ImageView ivImagePost;
public TextView tvTitle;
public LinearLayout llItemPost;
public TextView tvLabel;
public ViewHolder(View itemView) {
super(itemView);
ivImagePost = (ImageView) itemView.findViewById(R.id.iv_image_post);
tvTitle = (TextView) itemView.findViewById(R.id.tv_blog_title);
llItemPost = (LinearLayout) itemView.findViewById(R.id.ll_item_post);
tvLabel = (TextView) itemView.findViewById(R.id.textview_label_adapter);
}
}
public static List<String> extractUrls(String input) {
List<String> result = new ArrayList<String>();
Pattern pattern = Pattern.compile(
"\\b(((ht|f)tp(s?)\\:\\/\\/|~\\/|\\/)|www.)" +
"(\\w+:\\w+#)?(([-\\w]+\\.)+(com|org|net|gov" +
"|mil|biz|info|mobi|name|aero|jobs|museum" +
"|travel|[a-z]{2}))(:[\\d]{1,5})?" +
"(((\\/([-\\w~!$+|.,=]|%[a-f\\d]{2})+)+|\\/)+|\\?|#)?" +
"((\\?([-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?" +
"([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)" +
"(&(?:[-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?" +
"([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)*)*" +
"(#([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)?\\b");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
result.add(matcher.group());
}
return result;
}
}
This code of MainActivity.java
private void getListPost(){
mSwipeRefreshLayout.setRefreshing(true);
String nextPageToken = GlobalFunction.getStrings(this, GlobalVariable.keySharedPreference.TOKEN_PAGINATION);
BloggerApiService apiService = BloggerApiClient.getClient().create(BloggerApiService.class);
Call<ListPostModel> call = apiService.getListPost(GlobalVariable.APP_KEY_V3);
call.enqueue(new Callback<ListPostModel>() {
#Override
public void onResponse(Call<ListPostModel> call, Response<ListPostModel> response) {
ListPostModel listpost = response.body();
initDataView(listpost);
mSwipeRefreshLayout.setRefreshing(false);
}
#Override
public void onFailure(Call<ListPostModel> call, Throwable t) {
mSwipeRefreshLayout.setRefreshing(false);
Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
/**
* this method used for post (next page)
*/
private void getNextListPost(){
mSwipeRefreshLayout.setRefreshing(true);
String nextPageToken = GlobalFunction.getStrings(this, GlobalVariable.keySharedPreference.TOKEN_PAGINATION);
BloggerApiService apiService = BloggerApiClient.getClient().create(BloggerApiService.class);
Call<ListPostModel> call = apiService.getNexPageListPost(GlobalVariable.APP_KEY_V3,nextPageToken);
call.enqueue(new Callback<ListPostModel>() {
#Override
public void onResponse(Call<ListPostModel> call, Response<ListPostModel> response) {
ListPostModel listpost = response.body();
initDataView2(listpost);
mSwipeRefreshLayout.setRefreshing(false);
}
#Override
public void onFailure(Call<ListPostModel> call, Throwable t) {
mSwipeRefreshLayout.setRefreshing(false);
Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onRefresh() {
mListPost.clear();
getListPost();
}
private void initDataView(ListPostModel listpost){
GlobalFunction.saveString(this,GlobalVariable.keySharedPreference.TOKEN_PAGINATION, listpost.getNextPageToken());
mListPost.addAll(listpost.getListItemsPost());
mPostAdapter = new ListPostAdapter(mListPost, this) {
#Override
public void load() {
ItemPostModel item = mListPost.get(mListPost.size()-1);
getNextListPost();
}
};
mRecyclerviewPost.setAdapter(mPostAdapter);
//mPostAdapter.notifyDataSetChanged();
mRecyclerviewPost.setHasFixedSize(true);
mGridViewLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
mGridViewLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
mRecyclerviewPost.setLayoutManager(mGridViewLayoutManager);
}
/**
* this method used for set view (next page)
* #param listpost
*/
private void initDataView2(ListPostModel listpost){
GlobalFunction.saveString(this,GlobalVariable.keySharedPreference.TOKEN_PAGINATION, listpost.getNextPageToken());
final String nextPageToken = GlobalFunction.getStrings(this, GlobalVariable.keySharedPreference.TOKEN_PAGINATION);
List<ItemPostModel> itemNextPost = listpost.getListItemsPost();
// itemNextPost.addAll(mListPost);
mListPost.addAll(itemNextPost);
mPostAdapter = new ListPostAdapter(mListPost, this) {
#Override
public void load() {
ItemPostModel item = mListPost.get(mListPost.size()-1);
if(nextPageToken==null){
}else{
getNextListPost();
}
}
};
mRecyclerviewPost.setAdapter(mPostAdapter);
mRecyclerviewPost.setHasFixedSize(true);
mGridViewLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
mGridViewLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
mRecyclerviewPost.setLayoutManager(mGridViewLayoutManager);
an
}
Any suggestion will be appreciated.
The problem with the code is, your resetting the complete data again into your adapter and setting it to RecyclerView again, That's why it is re instanced everything in RecyclerView and i.e scrolling to top. Instead of that you can try something like just add /append the updated data into the list ( where you holds the data ) and then just call the adapter.notifyDataSetChanged(); method. automatically it will add the updated data and you no need to set the recycler view again.
Probably for your case you need to change your initDataView2() mehtod like below
private void initDataView2(ListPostModel listpost){
GlobalFunction.saveString(this,GlobalVariable.keySharedPreference.TOKEN_PAGINATION, listpost.getNextPageToken());
mListPost.addAll(listpost.getListItemsPost());\
mPostAdapter .notifyDataSetChanged();
}
For nextPageToken thing you can move into your main code onCreate() or the initDataView() method where you already have the adapter initialization
like this,
private void initDataView(ListPostModel listpost){
GlobalFunction.saveString(this,GlobalVariable.keySharedPreference.TOKEN_PAGINATION, listpost.getNextPageToken());
final String nextPageToken = GlobalFunction.getStrings(this, GlobalVariable.keySharedPreference.TOKEN_PAGINATION);
mListPost.addAll(listpost.getListItemsPost());
mPostAdapter = new ListPostAdapter(mListPost, this) {
#Override
public void load() {
ItemPostModel item = mListPost.get(mListPost.size()-1);
if(nextPageToken==null){
}else{
getNextListPost();
}
}
};
mRecyclerviewPost.setAdapter(mPostAdapter);
//mPostAdapter.notifyDataSetChanged();
mRecyclerviewPost.setHasFixedSize(true);
mGridViewLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
mGridViewLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
mRecyclerviewPost.setLayoutManager(mGridViewLayoutManager);
}
I hope it will work :)
Related
I am trying to add flickr images with their titles in a recycler view the problem encountering me is that I want to pass the images and their titles to FlickrPhoto class in the loop (please take a look above to see what I'm saying ) if you notice this line photos.add(new FlickrPhoto(title,uri)); you will see that it inside loop in enqueue function which is at the background task , So I recognize that line doesn't executed .. but when I've tried after enqueue function it works for me but ofcourse for just one image because , I'm outside the loop. I've tried many solutions but all fails
Here is my PhotoListActivity
package com.example.karim.bluecrunch;
/**
* Created by karim on 8/26/16.
*/
public class FlickrPhoto {
String title , image ;
public FlickrPhoto(String title , String image )
{
this.image = image;
this.title = title;
}
}
and here is my PhotosRecyclerViewAdapter
package com.example.karim.bluecrunch;
public class PhotosRecyclerViewAdapter extends RecyclerView.Adapter<PhotosRecyclerViewAdapter.PhotoViewHolder> {
List<FlickrPhoto> photos = Collections.emptyList();
Context context;
LayoutInflater inflater;
public PhotosRecyclerViewAdapter(Context context , List<FlickrPhoto> photos)
{
this.context = context;
this.photos = photos;
inflater = LayoutInflater.from(context);
}
#Override
public PhotoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View row = inflater.inflate(R.layout.single_row,parent,false);
return new PhotoViewHolder(row);
}
#Override
public void onBindViewHolder(PhotoViewHolder holder, int position) {
FlickrPhoto photo = photos.get(position);
holder.textView.setText(photo.title);
Uri uri = Uri.parse(photo.image);
Glide.with(context).load(uri).placeholder(R.drawable.image_placeholder).crossFade().into(holder.imageView);
}
#Override
public int getItemCount() {
return photos.size();
}
public class PhotoViewHolder extends RecyclerView.ViewHolder {
private TextView textView;
private ImageView imageView;
public PhotoViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.photoTitle);
imageView = (ImageView) itemView.findViewById(R.id.flickrPhoto);
}
}
}
and here is my PhotosListActivity
package com.example.karim.bluecrunch;
public class PhotosListActivity extends AppCompatActivity {
List<FlickrPhoto> photos ;
ArrayList<String> photosTitles;
ArrayList<String> photoURLS;
String title;
String uri;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photos_list);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.photo_recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(getApplicationContext(),2));
PhotosRecyclerViewAdapter adapter = new PhotosRecyclerViewAdapter(this,getPhotos());
recyclerView.setAdapter(adapter);
}
public List<FlickrPhoto> getPhotos()
{
photos = new ArrayList<>();
final String API_KEY = "fdac2e9676991ac53b34651adab52518";
final String METHOD = "flickr.photos.search";
final String AUTH_TOKEN = "72157671978046542-6e266595ffed01f8";
final String API_SIG = "58e08d365779a8f2e946a2b5320199e2";
final String FORMAT = "json";
final int CALL_BACK = 1;
HandleRetrofit handleRetrofit = HandleRetrofit.retrofit.create(HandleRetrofit.class);
Call<Photos> call = handleRetrofit.Photos(METHOD,API_KEY,FORMAT,CALL_BACK,AUTH_TOKEN,API_SIG);
call.enqueue(new Callback<Photos>() {
#Override
public void onResponse(Call<Photos> call, Response<Photos> response) {
Log.d("MainActivity", "Status Code = " + response.code());
PhotosRetrofit photosRetrofit = response.body().photos;
for (int i = 0; i < photosRetrofit.getPhoto().size(); i++) {
uri="https://farm"+photosRetrofit.getPhoto().get(i).getFarm()+".staticflickr.com/"+
photosRetrofit.getPhoto().get(i).getServer()+"/"+
photosRetrofit.getPhoto().get(i).getId()+"_" +
photosRetrofit.getPhoto().get(i).getSecret()+".jpg";
title= photosRetrofit.getPhoto().get(i).getTitle();
photos.add(new FlickrPhoto(title,uri));
Log.w(">>>>>>>>>>>","https://farm"+photosRetrofit.getPhoto().get(i).getFarm()+".staticflickr.com/"+
photosRetrofit.getPhoto().get(i).getServer()+"/"+
photosRetrofit.getPhoto().get(i).getId()+"_" +
photosRetrofit.getPhoto().get(i).getSecret()+".jpg");
}
}
#Override
public void onFailure(Call<Photos> call, Throwable t) {
Toast.makeText(PhotosListActivity.this,"Error :"+t.getMessage(),Toast.LENGTH_LONG).show();
Log.w("---___--- Error ",t.getMessage());
}
});
/* Log.w("Hello",uri.toString());
Log.w("Hello",title.toString());*/
photos.add(new FlickrPhoto("karim","https://farm9.staticflickr.com/8450/29141814932_a62977990d.jpg"));
return photos;
}
}
All things works fine , but the main problem at this line in my Activity
photos.add(new FlickrPhoto(title,uri));
I think it can't be done in the background tasks because this line
photos.add(new FlickrPhoto("karim","https://farm9.staticflickr.com/8450/29141814932_a62977990d.jpg"));
works fine after the enqueue function , but I don't know how to do such a trick to solve this problem .
EDIT
Please note that The loop
for (int i = 0; i < photosRetrofit.getPhoto().size(); i++)
{
uri="https://farm"... ;
title= ..;
**photos.add(new FlickrPhoto(title,uri));**
Log.w(">>>>>>>>>>>","https://farm"+photosRetrofit.getPhoto().get(i).getFarm()+".staticflickr.com/"+photosRetrofit.getPhoto().get(i).getServer()+"/"+photosRetrofit.getPhoto().get(i).getId()+"_" +photosRetrofit.getPhoto().get(i).getSecret()+".jpg");
}
doesn't skipped because , if you try to log either title or uri inside the loop it will shown successfully in Logcat . The main problem is that this line of code photos.add(new FlickrPhoto(title,uri)); passes the data "title and uri " to the FlickrPhoto class's constructor and this operation is in the enqueue -> onResponse (Background task) and I don't know why it is not working
It is excepted to load data (Images & Titles ) into recycler view but Actually nothing happens and
I've tried a couple of things
First , before return photos () arrayList at the PhotosListActivity class I've tried to pass a fixed data to the FlickrPhoto class's Constructor And it works and images shown into recycler view
Second , I've tried to add a default constructor and setter , getter for title and image to FlickrPhoto class and initialize it on OnCreate function at PhotosListActivity class then on the enqueue I used my setter and getter and it works and load all data successfully but when I've try to run again everything gone ! :( , and I don't know why
doing setters and getters was like this
FlickrPhoto.java class
package com.example.karim.bluecrunch;
/**
* Created by karim on 8/26/16.
*/
public class FlickrPhoto {
String title , image ;
public FlickrPhoto(){}
public FlickrPhoto(String title , String image )
{
this.image = image;
this.title = title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
}
and PostListActivity Class
package com.example.karim.bluecrunch;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.http.Url;
public class PhotosListActivity extends AppCompatActivity {
List<FlickrPhoto> photos ;
ArrayList<String> photosTitles;
ArrayList<String> photoURLS;
FlickrPhoto flickrPhoto ;
String title;
String uri;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photos_list);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.photo_recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(this,2));
PhotosRecyclerViewAdapter adapter = new PhotosRecyclerViewAdapter(this,getPhotos());
recyclerView.setAdapter(adapter);
flickrPhoto = new FlickrPhoto();
}
public List<FlickrPhoto> getPhotos()
{
photos = new ArrayList<>();
final String API_KEY = "fdac2e9676991ac53b34651adab52518";
final String METHOD = "flickr.photos.search";
final String AUTH_TOKEN = "72157671978046542-6e266595ffed01f8";
final String API_SIG = "58e08d365779a8f2e946a2b5320199e2";
final String FORMAT = "json";
final int CALL_BACK = 1;
HandleRetrofit handleRetrofit = HandleRetrofit.retrofit.create(HandleRetrofit.class);
Call<Photos> call = handleRetrofit.Photos(METHOD,API_KEY,FORMAT,CALL_BACK,AUTH_TOKEN,API_SIG);
call.enqueue(new Callback<Photos>() {
#Override
public void onResponse(Call<Photos> call, Response<Photos> response) {
Log.d("MainActivity", "Status Code = " + response.code());
PhotosRetrofit photosRetrofit = response.body().photos;
//photosRetrofit.getPhoto().size()
for (int i = 0; i < photosRetrofit.getPhoto().size(); i++) {
uri="https://farm"+photosRetrofit.getPhoto().get(i).getFarm()+".staticflickr.com/"+
photosRetrofit.getPhoto().get(i).getServer()+"/"+
photosRetrofit.getPhoto().get(i).getId()+"_" +
photosRetrofit.getPhoto().get(i).getSecret()+".jpg";
title= photosRetrofit.getPhoto().get(i).getTitle();
flickrPhoto.setImage(uri);
flickrPhoto.setTitle(title);
photos.add(new FlickrPhoto(flickrPhoto.getTitle(),flickrPhoto.getImage()));
Log.w("++++++++++++",photosRetrofit.getPhoto().get(i).getTitle());
Log.w(">>>>>>>>>>>","https://farm"+photosRetrofit.getPhoto().get(i).getFarm()+".staticflickr.com/"+
photosRetrofit.getPhoto().get(i).getServer()+"/"+
photosRetrofit.getPhoto().get(i).getId()+"_" +
photosRetrofit.getPhoto().get(i).getSecret()+".jpg");
}
}
#Override
public void onFailure(Call<Photos> call, Throwable t) {
Toast.makeText(PhotosListActivity.this,"Error :"+t.getMessage(),Toast.LENGTH_LONG).show();
Log.w("---___--- Error ",t.getMessage());
}
});
/* Log.w("Hello",uri.toString());
Log.w("Hello",title.toString());*/
photos.add(new FlickrPhoto("karim","https://farm9.staticflickr.com/8450/29141814932_a62977990d.jpg"));
return photos;
}
}
The problem is that RecyclerView does not automatically update after adding elements to the array list with its data. You need to call notifyDataSetChanged() on your adapter to force this update.
I have an app that uses RecyclerView. When user selects "Add New Row" from the options menu, I output a test message to the screen, and some values get appended to two different ArrayLists. That part is working great, as I have confirmed these values are successfully added by looking at the ArrayList values using the debugger.
Anyhow I am not able to get the RecyclerView to redraw the screen and show the new information. My attempt to redraw / update the screen is by using this code (line 78 of MainActivity.java):
//call notify data set changed method for the adapter
adapter.notifyDataSetChanged();
Maybe I am not calling notifyDataSetChanged on the same adapter that actually is used for RecycleView??
Here is the complete code for MainActivity.java (see options menu code at end):
package com.joshbgold.ironmax;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private static final int ADD_ROW = 1; //used for case statement statement to select menu item
private RecyclerView recyclerView;
public Exercises exercises = new Exercises();
public ExerciseRow adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
assert getSupportActionBar() != null;
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowHomeEnabled(true);
actionBar.setIcon(R.mipmap.barbell);
actionBar.setTitle(" " + "Iron Max");
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
//ExerciseRow adapter = new ExerciseRow(this);
adapter = new ExerciseRow(this);
recyclerView.setAdapter(adapter);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
/* MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.add_row, menu);
return super.onCreateOptionsMenu(menu);*/
menu.add(0, ADD_ROW, 0, "Add New Row");
menu.getItem(0).setIcon(R.drawable.plus);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.getItem(0).setIcon(R.drawable.plus);
return super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int length;
switch (item.getItemId()) {
case 1:
Toast msg = Toast.makeText(MainActivity.this, "Test code for adding an exercise", Toast.LENGTH_LONG);
msg.show();
exercises.addExercise("Some exercise");
exercises.addPersonalBest(500);
length = exercises.getExercisesArrayLength();
//call notify data set changed method for the adapter
adapter.notifyItemInserted(length - 1);
adapter.notifyDataSetChanged();
return super.onOptionsItemSelected(item);
default:
return super.onOptionsItemSelected(item);
}
}
}
Here is Exercises.java:
import android.support.v7.app.AppCompatActivity;
import java.util.ArrayList;
public class Exercises extends AppCompatActivity {
public ArrayList<String> exercisesArrayList = new ArrayList<>(); //stores all the lifts
private ArrayList<Integer> personalBestsArrayList = new ArrayList<>(); //stores personal bests in pounds
public ArrayList<String> getExercisesArray() { //returns the whole exercises arraylist
return exercisesArrayList;
}
public ArrayList<Integer> getPersonalBests() { //returns the whole personal bests arraylist
return personalBestsArrayList;
}
public String getExercise(int position) { //returns individual exercise from array
return exercisesArrayList.get(position);
}
public void addExercise(String exercise) {
exercisesArrayList.add(exercise);
}
public void removeExercise(int position) {
exercisesArrayList.remove(position);
}
public void editExercise(int position, String exercise) {
exercisesArrayList.set(position, exercise);
}
public int getExercisesArrayLength() {
return exercisesArrayList.size();
}
public Integer getPersonalBest(int position) { //returns individual personal best from array
return personalBestsArrayList.get(position);
}
public void addPersonalBest(int personalBest) {
personalBestsArrayList.add(personalBest);
}
public void removePersonalBest(int position) {
personalBestsArrayList.remove(position);
}
public void editPersonalBest(int position, int personalBest) {
personalBestsArrayList.set(position, personalBest);
}
public Exercises() {} //constructor
}
Here is ExerciseRow.java:
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class ExerciseRow extends RecyclerView.Adapter<ExerciseRow.ExerciseViewHolder> {
String exerciseName = "burpee";
String exercisePR = "100"; // user's personal record for this exercise in pounds
private Context context;
Exercises exercises = new Exercises();
public ExerciseRow(Context context) {
this.context = context;
}
#Override
public ExerciseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.exercise_list_item, parent, false);
return new ExerciseViewHolder(view);
}
#Override
public void onBindViewHolder(ExerciseViewHolder holder, int position) {
ArrayList<String> exercisesArray = exercises.getExercisesArray();
holder.bindExercises(exercisesArray.get(position));
}
#Override
public int getItemCount() {
return exercises.getExercisesArrayLength();
}
public class ExerciseViewHolder extends RecyclerView.ViewHolder {
public TextView exerciseNameTextView;
public TextView personalRecordTextView;
public ImageView edit_Icon;
public ImageView percentages_Icon;
public ImageView trash_Icon;
public ImageView plus_icon;
public ImageView facebook_icon;
public ImageView twitter_icon;
public ExerciseViewHolder(View itemView) {
super(itemView);
exerciseNameTextView = (TextView) itemView.findViewById(R.id.list_item_exercise_textview);
personalRecordTextView = (TextView) itemView.findViewById(R.id.list_item_amount_textview);
edit_Icon = (ImageView) itemView.findViewById(R.id.list_item_pencil);
percentages_Icon = (ImageView) itemView.findViewById(R.id.list_item_percent);
trash_Icon = (ImageView) itemView.findViewById(R.id.list_item_trash);
plus_icon = (ImageView) itemView.findViewById(R.id.list_item_plus);
facebook_icon = (ImageView) itemView.findViewById(R.id.list_item_facebook);
twitter_icon = (ImageView) itemView.findViewById(R.id.list_item_twitter);
View.OnClickListener plus = new View.OnClickListener() {
#Override
public void onClick(View view) {
// allow user to add a new exercise and personal best
exercises.addExercise("Some exercise");
exercises.addPersonalBest(500);
notifyDataSetChanged();
}
};
View.OnClickListener edit = new View.OnClickListener() {
#Override
public void onClick(View view) {
int position = getLayoutPosition(); //use getAdapterPosition() if getLayoutPosition causes a problem
if (position == 0) { //prevent user from deleting the first row.
Toast.makeText(context, "Sorry, example row cannot be edited.", Toast.LENGTH_LONG).show();
}
else{
editRow(position); //edit the row at the current position
notifyDataSetChanged();
}
}
};
View.OnClickListener percentages = new View.OnClickListener() {
#Override
public void onClick(View view) {
//get exercise name
exerciseName = exerciseNameTextView.getText().toString();
exercisePR = personalRecordTextView.getText().toString();
//show percentages layout
startPercentagesActivity(exerciseName, exercisePR);
}
};
View.OnClickListener trash = new View.OnClickListener() {
#Override
public void onClick(View view) {
int position = getLayoutPosition(); //use getAdapterPosition() if getLayoutPosition causes a problem
if (position == 0) { //prevent user from deleting the first row.
Toast.makeText(context, "Sorry, example row cannot be deleted.", Toast.LENGTH_LONG).show();
} else {
exercises.removeExercise(position);
exercises.removePersonalBest(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, exercises.getExercisesArrayLength());
}
}
};
plus_icon.setOnClickListener(plus);
edit_Icon.setOnClickListener(edit);
percentages_Icon.setOnClickListener(percentages);
trash_Icon.setOnClickListener(trash);
}
public void bindExercises(String exercises) {
Exercises exercisesObject = new Exercises();
exerciseNameTextView.setText(exercisesObject.getExercise(getAdapterPosition()));
personalRecordTextView.setText((exercisesObject.getPersonalBest(getAdapterPosition())).toString() + " pounds");
}
}
private void startPercentagesActivity(String some_exercise, String personal_record) {
Intent intent = new Intent(context, PercentagesActivity.class);
intent.putExtra("exerciseName", some_exercise);
intent.putExtra("personalRecord", personal_record);
context.startActivity(intent);
}
protected void editRow(final int position) {
LayoutInflater layoutInflater = LayoutInflater.from(context);
//text_entry is an Layout XML file containing two text field to display in alert dialog
final View textEntryView = layoutInflater.inflate(R.layout.text_entry, null);
final EditText liftName = (EditText) textEntryView.findViewById(R.id.liftNameEditText);
final EditText PersonalBestInPounds = (EditText) textEntryView.findViewById(R.id.personalBestEditText);
final AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setIcon(R.mipmap.barbell)
.setTitle("Please make your changes:")
.setView(textEntryView)
.setPositiveButton("Save",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//retrieve the user's input
String lift = liftName.getText().toString();
int personalBest = Integer.parseInt(PersonalBestInPounds.getText().toString());
//save the user's input to the appropriate arrays
exercises.editExercise(position, lift);
exercises.editPersonalBest(position, personalBest);
}
})
.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
}
});
alert.show();
}
}
If you have read this far, I give you credit! Please understand I am completely new to RecyclerView, so whatever error(s) I have made could be something quite simple even.
The issue is that you have two different instances of ExercisesOne in MainActivity and another in ExerciseRow You are adding the data to the wrong one.
A few other things that may help you along your way.
Exercises Should not extend AppCompatActivity (or really anything as far as I can tell)
You should avoid saving a reference to the context (as you are doing in Exercises) it can great memory issues. Instead try one of the following.
A. Use the context to get the LayoutInflater in the constructor
B. Call parent.getContext() in onCreateViewHolder
I am using restapi to provide data to my app in android and for the database I am using phpmyadmin and do it in localhost, everything goes well, but when I am adding new data in database my recycleview cannot sync with newest data in database so when I swipe or scroll the newest data that I have just added appear as double in my recycleview and it will never end. I am using an emulator to run the apk.
If anyone can help me fix my code I really appreciate it. Here is my customVolleyRequest code:
public class CustomVolleyRequest {
private static CustomVolleyRequest customVolleyRequest;
private static Context context;
private RequestQueue requestQueue;
private ImageLoader imageLoader;
private CustomVolleyRequest(Context context) {
this.context = context;
this.requestQueue = getRequestQueue();
imageLoader = new ImageLoader(requestQueue,
new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap>
cache = new LruCache<String, Bitmap>(20);
#Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
#Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
}
public static synchronized CustomVolleyRequest getInstance(Context context) {
if (customVolleyRequest == null) {
customVolleyRequest = new CustomVolleyRequest(context);
}
return customVolleyRequest;
}
public RequestQueue getRequestQueue() {
if (requestQueue == null) {
Cache cache = new DiskBasedCache(context.getCacheDir(), 10 * 1024 * 1024);
Network network = new BasicNetwork(new HurlStack());
requestQueue = new RequestQueue(cache, network);
requestQueue.start();
}
return requestQueue;
}
public ImageLoader getImageLoader() {
return imageLoader;
}
}
This is my appconfig class for storing tag:
public class AppConfig {
// Server user login url
public static String URL_LOGIN = "http://192.168.0.13:80/task_manager/v1/login";
// Server user register url
public static String URL_REGISTER = "http://192.168.0.13/task_manager/v1/register";
//URL of my even
public static final String DATA_URL = "http://192.168.0.13/task_manager/v1/even/";
//Tags for my JSON
public static final String TAG_IMAGE_URL = "gambar";
public static final String TAG_JUDUL = "judul";
public static final String TAG_DESKRIPSI = "deskripsi";
public static final String TAG_DUIT = "duit";
public static final String TAG_PERSEN = "persen";
public static final String TAG_SISA_HARI = "sisahari";
}
This is getter and setter:
public class Event {
//Data Variables
private String imageUrl;
private String name;
private String rank;
private int realName;
private int createdBy;
private int firstAppearance;
//private ArrayList<String> powers;
//Getters and Setters
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
//GET AND SET JUDUL
public String getJudul() {
return name;
}
public void setJudul(String name) {
this.name = name;
}
//GET AND SET DESKRIPSI
public String getDeskripsi() {
return rank;
}
public void setDeskripsi(String rank) {
this.rank = rank;
}
//GET AND SET DUIT
public int getDuit() {
return realName;
}
public void setDuit(int realName) {
this.realName = realName;
}
//GET AND SET PERSEN
public int getPersen() {
return createdBy;
}
public void setPersen(int createdBy) {
this.createdBy = createdBy;
}
//GET AND SET SISA HARI
public int getSisaHari() {
return firstAppearance;
}
public void setSisaHari(int firstAppearance) {
this.firstAppearance = firstAppearance;
}
}
And this is my main fragment:
package com.anakacara.anakacara.Fragment;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.anakacara.anakacara.App.AppConfig;
import com.anakacara.anakacara.App.Event;
import com.anakacara.anakacara.Helper.CardAdapter;
import com.anakacara.anakacara.R;
import com.anakacara.anakacara.activity.DetailEvent;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* A simple {#link Fragment} subclass.
* Activities that contain this fragment must implement the
* {#link EventFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {#link EventFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class EventFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
//Creating a List of event
private List<Event> listEvent;
//Creating Views
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private RecyclerView.Adapter adapter;
private int requestCount = 1;
private int list;
private OnFragmentInteractionListener mListener;
private RequestQueue requestQueue;
private ProgressBar progressBar;
private SwipeRefreshLayout swipeRefreshLayout;
public EventFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment EventFragment.
*/
// TODO: Rename and change types and number of parameters
public static EventFragment newInstance(String param1, String param2) {
EventFragment fragment = new EventFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
private final RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(final RecyclerView recyclerView, final int newState) {
// code
}
#Override
public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) {
// code
if (isLastItemDisplaying(recyclerView)) {
//Calling the method getdata again
getData();
}
}
};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View x = inflater.inflate(R.layout.fragment_event,null);;
progressBar = (ProgressBar) x.findViewById(R.id.progressBar1);
recyclerView = (RecyclerView) x.findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
listEvent = new ArrayList<>();
requestQueue = Volley.newRequestQueue(getActivity());
swipeRefreshLayout = (SwipeRefreshLayout) x.findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setOnRefreshListener(this);
/**
* Showing Swipe Refresh animation on activity create
* As animation won't start on onCreate, post runnable is used
*/
swipeRefreshLayout.post(new Runnable() {
#Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
getData();
}
}
);
// listEvent = new ArrayList<>();
// requestQueue = Volley.newRequestQueue(getActivity());
//Calling method to get data
//this method is called twice as if you see
// getData();
// swipeRefreshLayout= (SwipeRefreshLayout) x.findViewById(R.id.swipeRefreshLayout);
// swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
// {
// #Override
// public void onRefresh()
// {
// if (isLastItemDisplaying(recyclerView)) {
////Calling the method getdata again
// getData();
// progressBar.setVisibility(View.GONE);
// }
// }
// });
recyclerView.addOnScrollListener(rVOnScrollListener);
adapter = new CardAdapter(listEvent, getActivity());
//Adding adapter to recyclerview
recyclerView.setAdapter(adapter);
RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator();
itemAnimator.setAddDuration(1000);
itemAnimator.setRemoveDuration(1000);
recyclerView.setItemAnimator(itemAnimator);
recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), recyclerView, new ClickListener() {
#Override
public void onClick(View view, int position) {
Intent intent = new Intent(getActivity(), DetailEvent.class);
intent.putExtra("event_judul", listEvent.get(position).getJudul()); //you can name the keys whatever you like
intent.putExtra("event_deskripsi", listEvent.get(position).getDeskripsi()); //note that all these values have to be primitive (i.e boolean, int, double, String, etc.)
intent.putExtra("event_duit", listEvent.get(position).getDuit());
intent.putExtra("event_persen", listEvent.get(position).getPersen()); //note that all these values have to be primitive (i.e boolean, int, double, String, etc.)
intent.putExtra("event_sisa_hari", listEvent.get(position).getSisaHari());
intent.putExtra("event_image", listEvent.get(position).getImageUrl());
startActivity(intent);
}
#Override
public void onLongClick(View view, int position) {
Toast.makeText(getActivity(), "Kepencet Lama " + position, Toast.LENGTH_LONG).show();
}
}));
return x;
}
private JsonArrayRequest getDataFromServer(int requestCount) {
//Initializing ProgressBar
swipeRefreshLayout.setRefreshing(true);
//Displaying Progressbar
progressBar.setVisibility(View.VISIBLE);
// getActivity().getParent().setProgressBarIndeterminateVisibility(true);
//JsonArrayRequest of volley
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(AppConfig.DATA_URL + String.valueOf(requestCount),
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
//Calling method parseData to parse the json response
parseData(response);
//Hiding the progressbar
progressBar.setVisibility(View.GONE);
swipeRefreshLayout.setRefreshing(false);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
progressBar.setVisibility(View.GONE);
//If an error occurs that means end of the list has reached
Toast.makeText(getActivity(), "No More Items Available"+list, Toast.LENGTH_SHORT).show();
swipeRefreshLayout.setRefreshing(false);
}
});
//Returning the request
return jsonArrayRequest;
}
private void getData() {
//Adding the method to the queue by calling the method getDataFromServer
requestQueue.add(getDataFromServer(requestCount));
//Incrementing the request counter
}
//This method will parse json data
private void parseData(JSONArray array) {
for (int i = 0; i < array.length(); i++) {
Event event = new Event();
JSONObject json = null;
try {
json = array.getJSONObject(i);
event.setJudul(json.getString(AppConfig.TAG_JUDUL));
event.setDeskripsi(json.getString(AppConfig.TAG_DESKRIPSI));
event.setDuit(json.getInt(AppConfig.TAG_DUIT));
event.setPersen(json.getInt(AppConfig.TAG_PERSEN));
event.setSisaHari(json.getInt(AppConfig.TAG_SISA_HARI));
event.setImageUrl(json.getString(AppConfig.TAG_IMAGE_URL));
event.setTotal(json.getInt(AppConfig.TAG_TOTAL));
} catch (JSONException e) {
e.printStackTrace();
}
listEvent.add(event);
}
adapter.notifyDataSetChanged();
if( requestCount <= listEvent.get(0).getTotal()) {
requestCount++;
}
//Finally initializing our adapter
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
#Override
public void onRefresh() {
if (isLastItemDisplaying(recyclerView)) {
getData();
}
}
class RecyclerTouchListener implements RecyclerView.OnItemTouchListener{
private GestureDetector mGestureDetector;
private ClickListener mClickListener;
public RecyclerTouchListener(final Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.mClickListener = clickListener;
mGestureDetector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener(){
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
#Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(),e.getY());
if (child!=null && clickListener!=null){
clickListener.onLongClick(child, recyclerView.getChildAdapterPosition(child));
}
super.onLongPress(e);
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child!=null && mClickListener!=null && mGestureDetector.onTouchEvent(e)){
mClickListener.onClick(child, rv.getChildAdapterPosition(child));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
private boolean isLastItemDisplaying(RecyclerView recyclerView) {
if (recyclerView.getAdapter().getItemCount() != 0) {
int lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
if (lastVisibleItemPosition != RecyclerView.NO_POSITION && lastVisibleItemPosition == recyclerView.getAdapter().getItemCount() - 1)
return true;
}
return false;
}
private RecyclerView.OnScrollListener rVOnScrollListener = new RecyclerView.OnScrollListener(){
#Override
public void onScrollStateChanged(RecyclerView recyclerView,
int newState) {
super.onScrollStateChanged(recyclerView, newState);
// Toast.makeText(getApplicationContext(),
// Config.DATA_URL+String.valueOf(requestCount), Toast.LENGTH_LONG).show();
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (isLastItemDisplaying(recyclerView)) {
//Calling the method getdata again
getData();
progressBar.setVisibility(View.GONE);
}
}
};
public static interface ClickListener{
public void onClick(View view, int position);
public void onLongClick(View view, int position);
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
Here is my adapter:
package com.anakacara.anakacara.Helper;
/**
* Created by Philipus on 05/03/2016.
*/
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.anakacara.anakacara.App.CustomVolleyRequest;
import com.anakacara.anakacara.App.Event;
import com.anakacara.anakacara.R;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;
import java.util.List;
/**
* Created by Belal on 11/9/2015.
*/
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> {
private ImageLoader imageLoader;
private Context context;
//List of eventes
List<Event> eventes;
public CardAdapter(List<Event> eventes, Context context){
super();
//Getting all the eventA
this.eventes = eventes;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.event_list, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Event event = eventes.get(position);
imageLoader = CustomVolleyRequest.getInstance(context).getImageLoader();
imageLoader.get(event.getImageUrl(), ImageLoader.getImageListener(holder.imageView, R.mipmap.ic_launcher, android.R.drawable.ic_dialog_alert));
holder.imageView.setImageUrl(event.getImageUrl(), imageLoader);
holder.textViewJudul.setText(event.getJudul());
holder.textViewRank.setText(event.getDeskripsi());
holder.textViewRealName.setText(String.valueOf(event.getDuit()));
holder.textViewCreatedBy.setText(String.valueOf(event.getPersen()));
holder.textViewFirstAppearance.setText(String.valueOf(event.getSisaHari()));
}
#Override
public int getItemCount() {
return eventes.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
public NetworkImageView imageView;
public TextView textViewJudul;
public TextView textViewRank;
public TextView textViewRealName;
public TextView textViewCreatedBy;
public TextView textViewFirstAppearance;
// public TextView textViewPowers;
public ViewHolder(View itemView) {
super(itemView);
imageView = (NetworkImageView) itemView.findViewById(R.id.imageViewHero);
textViewJudul = (TextView) itemView.findViewById(R.id.textViewJudul);
textViewRank= (TextView) itemView.findViewById(R.id.textViewRank);
textViewRealName= (TextView) itemView.findViewById(R.id.textViewRealName);
textViewCreatedBy= (TextView) itemView.findViewById(R.id.textViewCreatedBy);
textViewFirstAppearance= (TextView) itemView.findViewById(R.id.textViewFirstAppearance);
}
}
}
use onRequestFinishedLitener of Volley queue to notifay data set changed.
class RequestListener implements RequestQueue.RequestFinishedListener {
#Override
public void onRequestFinished(Request request) {
adapter.notifyDatasetChanged();
}
}
Then
RequestListener rl = new RequestListener();
volley_queue.addRequestFinishedListener(listener);
if you want to load new data when list comes to the end. you will have to make pages in your API which lying on server. you will have to access page by page. As per the list reaches to end you call parseJson for next page.
you will have call your parseJSON method with new link which contains next data. For this you will have add OnScrollListener of ReyclerView.
class RecyclerViewOnScroll extends RecyclerView.OnScrollListener
{
int temp_page_position;
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if ((list.size()-1 == layoutManager.findLastVisibleItemPosition())) {
// Enters to block when last item of RecyclerView has appeared.
}
}
}
I am making a app with RecylerView populated with data form an online server. It has a custom layout with a favorite button that when clicked the icon changes.I have a problem trying to save state of a selected view on the RecyclerView. The RecyclerView does not save the selected state on scrolling back up. Kindly help.
Model Class
package com.smartdevelopers.kandie.nicedrawer.newsModel;
/**
* Created by 4331 on 25/09/2015.
*/
public class Latest {
public String excerpt;
public String articleImage;
public String articleUrl;
public boolean mfavourite;
public boolean isFavourite(){
return mfavourite;
}
public String getArticleUrl() {
return articleUrl;
}
public void setArticleUrl(String articleUrl) {
this.articleUrl = articleUrl;
}
public String getExcerpt() {
return excerpt;
}
public void setExcerpt(String excerpt) {
this.excerpt = excerpt;
}
public String getArticleImage() {
return articleImage;
}
public void setArticleImage(String articleImage) {
this.articleImage = articleImage;
}
}
Adapter
package com.smartdevelopers.kandie.nicedrawer.newsAdapter;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.smartdevelopers.kandie.nicedrawer.R;
import com.smartdevelopers.kandie.nicedrawer.ReadArticleActivity;
import com.smartdevelopers.kandie.nicedrawer.newsModel.Latest;
import com.smartdevelopers.kandie.nicedrawer.util.SharedPreferenceRecycler;
import java.util.HashMap;
import java.util.List;
/**
* Created by 4331 on 29/09/2015.
*/
public class OtherNewsAdapter extends RecyclerView.Adapter<OtherNewsAdapter.ViewHolder> {
private List<Latest> feedItemList;
private Context mContext;
private static final String TAG = "ActivityGplus";
SharedPreferenceRecycler sharedPreference = new SharedPreferenceRecycler();
public OtherNewsAdapter(Context context, List<Latest> feedItemList) {
this.feedItemList = feedItemList;
this.mContext = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.row_other_news, null);
ViewHolder mh = new ViewHolder(v);
return mh;
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, int i) {
final Latest feedItem = feedItemList.get(i);
ImageLoader imageLoader = ImageLoader.getInstance();
DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true)
.cacheOnDisc(true).resetViewBeforeLoading(true)
.showImageForEmptyUri(R.drawable.placeholder)
.showImageOnFail(R.drawable.placeholder)
.showImageOnLoading(R.drawable.placeholder).build();
//download and display image from url
imageLoader.displayImage(feedItem.getArticleImage(), viewHolder.thumbnail, options);
// Glide.with(mContext).load(feedItem.getArticleImage())
// .error(R.drawable.placeholder)
// .placeholder(R.drawable.placeholder)
// .into(viewHolder.thumbnail);
viewHolder.title.setText(Html.fromHtml(feedItem.getExcerpt()));
viewHolder.articleUrl.setText(Html.fromHtml(feedItem.getArticleUrl()));
viewHolder.title.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, ReadArticleActivity.class);
intent.putExtra("articleUrl", viewHolder.articleUrl.getText().toString());
mContext.startActivity(intent);
}
});
/*If a product exists in shared preferences then set heart_red drawable
* and set a tag*/
if (checkFavoriteItem(feedItem)) {
viewHolder.favImage.setImageResource(R.drawable.heart_red);
viewHolder.favImage.setSelected(true);
viewHolder.favImage.setTag("red");
hashMap.get(i);
}
else {
viewHolder.favImage.setImageResource(R.drawable.heart_grey);
viewHolder.favImage.setSelected(false);
viewHolder.favImage.setTag("grey");
}
viewHolder.title.setTag(viewHolder);
viewHolder.thumbnail.setId(R.id.otherImage);
viewHolder.articleUrl.setTag(viewHolder);
}
#Override
public int getItemCount() {
return (null != feedItemList ? feedItemList.size() : 0);
}
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
View.OnClickListener clickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
OtherNewsRowHolder holder = (OtherNewsRowHolder) view.getTag();
int position = holder.getPosition();
Latest feedItem = feedItemList.get(position);
Toast.makeText(mContext, feedItem.getExcerpt(), Toast.LENGTH_SHORT).show();
}
};
/*Checks whether a particular product exists in SharedPreferences*/
public boolean checkFavoriteItem(Latest checkProduct) {
boolean check = false;
List<Latest> favorites = sharedPreference.getFavorites(mContext);
if (favorites != null) {
for (Latest product : favorites) {
if (product.equals(checkProduct)) {
check = true;
break;
}
}
}
return check;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
protected ImageView thumbnail, favImage;
protected TextView title,articleUrl;
public ViewHolder(View itemView) {
super(itemView);
this.thumbnail = (ImageView) itemView.findViewById(R.id.otherImage);
this.title = (TextView) itemView.findViewById(R.id.otherExcerpt);
this.articleUrl=(TextView)itemView.findViewById(R.id.otherUrl);
this.favImage = (ImageView) itemView.findViewById(R.id.imgbtn_favorite);
favImage.setOnClickListener(this);
}
#Override
public void onClick(View v) {
String tag = favImage.getTag().toString();
if(!favImage.isSelected()) {
if (tag.equalsIgnoreCase("grey")) {
sharedPreference.addFavorite(mContext, feedItemList.get(getItemCount() - 1));
// Toast.makeText(mContext, mContext.getResources().getString(R.string.add_favr),
// Toast.LENGTH_SHORT).show();
//SnackBack
SnackbarManager.show(
Snackbar.with(mContext)
.text(R.string.add_favr)
.textColor(Color.WHITE)
.color(Color.RED)
.duration(Snackbar.SnackbarDuration.LENGTH_SHORT));
//End of SnackBack
favImage.setTag("red");
favImage.setImageResource(R.drawable.heart_red);
}
}
else {
sharedPreference.removeFavorite(mContext, feedItemList.get(getItemCount() - 1));
favImage.setTag("grey");
favImage.setImageResource(R.drawable.heart_grey);
//SnackBack
SnackbarManager.show(
Snackbar.with(mContext)
.text(R.string.remove_favr)
.textColor(Color.WHITE)
.color(Color.RED) .duration(Snackbar.SnackbarDuration.LENGTH_SHORT).animation(false));
//End of SnackBack
}
}
}
}
I ran into this problem a couple of days ago - the reason is that you have not updated your feedItemList with the applicable changes. If you update the list by setting some of the member variables (like setExcerpt()) of the Latest class once it has change, your recyclerview will work properly.
In my App I have a RecyclerView connected with an adapter but everytime my device changes from landscape to portrait or something else, the view get reloaded and I'll get to the top again. I want to save my scroll position to the last item I saw.
Here is what I tried:
I have a Fragment that extends from another fragment the state should be save within the MainPageFragment. I tried to save the last position loaded and put it into the savedState, it gets correctly retrieved but If I call "scrollToPosition" nothing happens.
package com.pr0.pr0grammreloaded.fragment;
/**
* Created by Dominik on 22.02.2015.
*/
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.pr0.pr0grammreloaded.MainActivity;
import com.pr0.pr0grammreloaded.R;
import com.pr0.pr0grammreloaded.config.FilterConfig;
import com.pr0.pr0grammreloaded.util.FixedRecyclerView;
import com.pr0.pr0grammreloaded.util.MainPageSaveParcelable;
import com.pr0.pr0grammreloaded.util.MyLayoutManager;
import com.pr0.pr0grammreloaded.util.RecyclerViewDelegate;
import java.util.ArrayList;
import uk.co.senab.actionbarpulltorefresh.extras.actionbarsherlock.PullToRefreshLayout;
import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh;
import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.AbsListViewDelegate;
/**
* Created by Dominik on 17.01.2015.
*/
public class MainPageFragment extends Pr0MainPageFragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static MainPageFragment newInstance(FilterConfig config) {
MainPageFragment fragment = new MainPageFragment();
fragment.config = config;
return fragment;
}
public MainPageFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if(savedInstanceState != null){
MainPageSaveParcelable saveParcelable = savedInstanceState.getParcelable("save");
View rootView = inflater.inflate(R.layout.mainpage_fragment, container, false);
recyclerView = (RecyclerView) rootView.findViewById(R.id.list);
adapter = new ItemAdapter();
itemList = saveParcelable.itemList;
config = saveParcelable.config;
// use better layout manager, maybe write our own?
recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.getActivity(), 3));
recyclerView.setAdapter(adapter);
restorePositon = saveParcelable.position;
mPullToRefreshLayout = (PullToRefreshLayout) rootView.findViewById(R.id.ptr_layout);
// Now setup the PullToRefreshLayout
ActionBarPullToRefresh.from(getActivity())
// Mark All Children as pullable
.allChildrenArePullable()
// Set a OnRefreshListener
.listener(new OnRefreshListener() {
#Override
public void onRefreshStarted(View view) {
if (!isBlocked()) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, MainPageFragment.newInstance(MainActivity.filterConfig))
.commit();
}
}
})
// Finally commit the setup to our PullToRefreshLayout
.useViewDelegate(FixedRecyclerView.class, new RecyclerViewDelegate())
.setup(mPullToRefreshLayout);
loadFeed();
// recyclerView.scrollToPosition(saveParcelable.);
return rootView;
}else {
View rootView = inflater.inflate(R.layout.mainpage_fragment, container, false);
recyclerView = (RecyclerView) rootView.findViewById(R.id.list);
adapter = new ItemAdapter();
itemList = new ArrayList<>();
// use better layout manager, maybe write our own?
recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.getActivity(), 3));
recyclerView.setAdapter(adapter);
mPullToRefreshLayout = (PullToRefreshLayout) rootView.findViewById(R.id.ptr_layout);
// Now setup the PullToRefreshLayout
ActionBarPullToRefresh.from(getActivity())
// Mark All Children as pullable
.allChildrenArePullable()
// Set a OnRefreshListener
.listener(new OnRefreshListener() {
#Override
public void onRefreshStarted(View view) {
if (!isBlocked()) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, MainPageFragment.newInstance(MainActivity.filterConfig))
.commit();
}
}
})
// Finally commit the setup to our PullToRefreshLayout
.useViewDelegate(FixedRecyclerView.class, new RecyclerViewDelegate())
.setup(mPullToRefreshLayout);
loadFeed();
return rootView;
}
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
#Override
public void onSaveInstanceState(Bundle state){
super.onSaveInstanceState(state);
restorePositon = lastPositon;
MainPageSaveParcelable saveParcelable = new MainPageSaveParcelable(itemList,firstChunk,config,restorePositon);
state.putParcelable("save",saveParcelable);
}
}
Pr0MainPageFragment
package com.pr0.pr0grammreloaded.fragment;
import android.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.pr0.pr0grammreloaded.MainActivity;
import com.pr0.pr0grammreloaded.R;
import com.pr0.pr0grammreloaded.api.Api;
import com.pr0.pr0grammreloaded.api.Feed;
import com.pr0.pr0grammreloaded.api.InstantDeserializer;
import com.pr0.pr0grammreloaded.config.FilterConfig;
import com.squareup.picasso.Picasso;
import org.joda.time.Instant;
import java.util.ArrayList;
import java.util.List;
import retrofit.RestAdapter;
import retrofit.converter.GsonConverter;
import rx.functions.Action1;
import uk.co.senab.actionbarpulltorefresh.extras.actionbarsherlock.PullToRefreshLayout;
import static rx.android.observables.AndroidObservable.bindActivity;
/**
* Created by Dominik on 22.02.2015.
*/
public class Pr0MainPageFragment extends Fragment {
protected ItemAdapter adapter;
protected boolean firstChunk = true;
protected List<Feed.Item> itemList;
protected FilterConfig config;
private boolean blockLoading = false;
protected PullToRefreshLayout mPullToRefreshLayout;
protected int restorePositon = 0;
protected int lastPositon = 0;
protected RecyclerView recyclerView;
protected boolean isRestore = false;
/**
* Loads the feed from pr0gramm. This should be put into some kind of service
* that is injected into our activities.
*/
protected void loadFeed() {
if(!blockLoading) {
blockLoading = true;
Log.e("Pr0","Blocked loading!");
Gson gson = new GsonBuilder()
.registerTypeAdapter(Instant.class, new InstantDeserializer())
.create();
Api api = new RestAdapter.Builder()
.setEndpoint("http://pr0gramm.com")
.setConverter(new GsonConverter(gson))
.setLogLevel(RestAdapter.LogLevel.BASIC)
.build()
.create(Api.class);
// perform api request in the background and call
// back to the main thread on finish
if (firstChunk) {
bindActivity(MainActivity.getActivity(), api.itemsGet(config.getFlag(), 1)).subscribe(new Action1<Feed>() {
#Override
public void call(Feed feed) {
// we are now back in the main thread
firstChunk = false;
handleFeedResponse(feed);
}
});
} else {
//Log.e("Pr0", "Loading after ID : " + itemList.get(0).getId());
for(int x = 0; x < itemList.size();x ++){
Log.e("Pr0", "POS: " + x + ", ID : " + itemList.get(x).getId());
}
bindActivity(MainActivity.getActivity(), api.olderGet(itemList.get(itemList.size() - 1).getPromoted(), config.getFlag(), 1)).subscribe(new Action1<Feed>() {
#Override
public void call(Feed feed) {
// we are now back in the main thread
handleFeedResponse(feed);
}
});
}
}
}
/**
* Display the elements from the feed
*
* #param feed The feed to display
*/
private void handleFeedResponse(Feed feed) {
// display feed now.
//Log.i("MainActivity", "Number of items: " + feed.getItems().size());
adapter.addItems(feed.getItems());
mPullToRefreshLayout.setRefreshComplete();
restorePostion();
}
protected class ItemAdapter extends RecyclerView.Adapter<ItemView> {
ItemAdapter() {
setHasStableIds(true);
}
#Override
public ItemView onCreateViewHolder(ViewGroup viewGroup, int i) {
LayoutInflater inflater = LayoutInflater.from(MainActivity.getActivity());
View view = inflater.inflate(R.layout.item_view, viewGroup, false);
return new ItemView(view);
}
#Override
public void onBindViewHolder(ItemView itemView, int i) {
// Log.e("Pr0","load id : " + i);
//Log.e("Pr0",String.valueOf(itemList.get(i).getId()));
String url = "http://thumb.pr0gramm.com/" + itemList.get(i).getThumb();
Picasso.with(getActivity())
.setIndicatorsEnabled(true);
Picasso.with(getActivity())
.load(url)
.into(itemView.image);
lastPositon = i;
Log.w("Pr0","last positon : " + i);
if(i > itemList.size() - 5){
//Log.e("Pr0","SIZE : " + itemList.size());
//Log.e("Pr0","End Reached Load After ID : " + itemList.get(0).getId());
loadFeed();
}
}
#Override
public int getItemCount() {
return itemList.size();
}
public void addItems(List<Feed.Item> itemsToAdd) {
int oldCount = itemList.size();
itemList.addAll(itemsToAdd);
notifyItemRangeInserted(oldCount, itemList.size());
/*
for(Feed.Item item : itemsToAdd) {
//Log.e("Pr0","Added image ID : " + item.getId());
int oldCount = itemList.size();
itemList.add(item);
notifyItemRangeInserted(oldCount, itemList.size());
}
*/
blockLoading = false;
Log.e("Pr0","Unblocked Loading");
//
}
#Override
public long getItemId(int position) {
return itemList.get(position).getId();
}
}
/**
* View holder for a view in the list of items
*/
private class ItemView extends RecyclerView.ViewHolder {
final ImageView image;
public ItemView(View itemView) {
super(itemView);
image = (ImageView) itemView.findViewById(R.id.image);
}
}
public boolean isBlocked(){
return this.blockLoading;
}
public void reset(){
itemList.clear();
firstChunk = true;
}
public void restorePostion(){
Log.w("Pr0", "Restore : " + restorePositon);
recyclerView.scrollToPosition(restorePositon);
}
}
And here is my Parcelable
package com.pr0.pr0grammreloaded.util;
import android.os.Parcel;
import android.os.Parcelable;
import com.pr0.pr0grammreloaded.account.User;
import com.pr0.pr0grammreloaded.api.Feed;
import com.pr0.pr0grammreloaded.config.FilterConfig;
import java.util.List;
/**
* Created by Dominik on 27.02.2015.
*/
public class MainPageSaveParcelable implements Parcelable{
public boolean firstChunk;
public List<Feed.Item> itemList;
public FilterConfig config;
public int position;
public MainPageSaveParcelable(List<Feed.Item> itemList, boolean firstChunk, FilterConfig config, int position){
this.firstChunk = firstChunk;
this.itemList = itemList;
this.config = config;
this.position = position;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(itemList);
dest.writeValue(config);
dest.writeValue(firstChunk);
dest.writeInt(position);
}
/** Static field used to regenerate object, individually or as arrays */
public static final Parcelable.Creator<MainPageSaveParcelable> CREATOR = new Parcelable.Creator<MainPageSaveParcelable>() {
public MainPageSaveParcelable createFromParcel(Parcel pc) {
return new MainPageSaveParcelable(pc);
}
public MainPageSaveParcelable[] newArray(int size) {
return new MainPageSaveParcelable[size];
}
};
/**Ctor from Parcel, reads back fields IN THE ORDER they were written */
public MainPageSaveParcelable(Parcel pc){
pc.readList(itemList,List.class.getClassLoader());
config = (FilterConfig) pc.readValue(FilterConfig.class.getClassLoader());
firstChunk = (boolean) pc.readValue(Boolean.class.getClassLoader());
position = pc.readInt();
}
}
you can use scroll listener and save first items or RecyclerView state on scrolling in SharedPreference or whatever you prefer
define these variables as private variables in your activity or fragment
int firstCompleteVisibleItemPosition;
int firstVisibleItemPosition;
Parcelable recyclerViewState;
use recyclerview on scroll listener
recyclerView.addOnScrollListener(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);
firstCompleteVisibleItemPosition = recyclerView.getLayoutManager().findFirstCompletelyVisibleItemPosition();
firstVisibleItemPosition = recyclerView.getLayoutManager().findFirstVisibleItemPosition();
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
}
});
and if orientation changes you can start your recyclerview by last position u saved by this method
linearLayoutManager.scrollToPositionWithOffset(firstVisibleItemPosition, 0);
or by last state of recyclerview
rv.getLayoutManager().onRestoreInstanceState(recyclerViewState);