How to set data in recyclerView with Retrofit in Android - java

I want to show some data into recyclerview and for this I am using the code displayed below.
When I run the application, it's not showing me any data into recyclerview and ListData.size() is 0, but in PostMan
I write below code for set into recyclerView :
InterfaceApi api = ApiClient.getClient().create(InterfaceApi.class);
Call<CommentResponse> call = api.getComments(sendData);
call.enqueue(new Callback<CommentResponse>() {
#Override
public void onResponse(Call<CommentResponse> call, Response<CommentResponse> response) {
if (response.body().getData() != null) {
commentModel.clear();
commentModel.addAll(response.body().getData());
commentsListAdapter.notifyDataSetChanged();
content_newsCommentsRecyclerView.setAdapter(commentsListAdapter);
}
}
#Override
public void onFailure(Call<CommentResponse> call, Throwable t) {
}
});
My Adapter codes:
public class CommentsListAdapter extends RecyclerView.Adapter<CommentsListAdapter.ViewHolder> {
private Context context;
private List<CommentData> model;
public CommentsListAdapter(Context context, List<CommentData> model) {
this.context = context;
this.model = model;
}
#Override
public CommentsListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_comment, parent, false);
return new CommentsListAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(final CommentsListAdapter.ViewHolder holder, final int position) {
holder.row_commentNameTxt.setText(Html.fromHtml(model.get(position).getOwner().getName()));
holder.row_commentCommentTxt.setText(Html.fromHtml(model.get(position).getText()));
Glide.with(context)
.load(model.get(position).getOwner().getImageUrl())
.placeholder(R.drawable.default_image)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.listener(new RequestListener<String, GlideDrawable>() {
#Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}
#Override
public boolean onResourceReady(GlideDrawable resource, String model,
Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
})
.into(holder.row_commentProfileImage);
holder.row_commentLikeTxt.setText(model.get(position).getLikeCount() + "");
holder.row_commentReplayTxt.setText(model.get(position).getRepliesCount() + "");
holder.row_commentDateTxt.setText(model.get(position).getSubmitDate() + " " + model.get(position).getSubmitTime());
}
#Override
public int getItemCount() {
return model.size();
}
public void addNewItem(List<CommentData> newContent) {
int start = this.model.size();
int end = newContent.size();
model.addAll(newContent);
notifyDataSetChanged();
}
public void clear() {
model.clear();
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private CircleImageView row_commentProfileImage;
private TextView row_commentNameTxt, row_commentCommentTxt, row_commentLikeTxt, row_commentReplayTxt, row_commentDateTxt;
public ViewHolder(View itemView) {
super(itemView);
row_commentProfileImage = (CircleImageView) itemView.findViewById(R.id.row_commentProfileImage);
row_commentNameTxt = (TextView) itemView.findViewById(R.id.row_commentNameTxt);
row_commentCommentTxt = (TextView) itemView.findViewById(R.id.row_commentCommentTxt);
row_commentLikeTxt = (TextView) itemView.findViewById(R.id.row_commentLikeTxt);
row_commentReplayTxt = (TextView) itemView.findViewById(R.id.row_commentReplayTxt);
row_commentDateTxt = (TextView) itemView.findViewById(R.id.row_commentDateTxt);
}
}
}
How can I display the data into recyclerView?

Create a method inside your Adapter class
//This will add all the items in the adapter's list
public void addAllItems(List<CommentData> items) {
model.addAll(items);
notifyDataSetChanged();
}
//In Adapter's Constructor do the following changes
public CommentsListAdapter(Context context, List<CommentData> model) {
this.context = context;
this.model = new ArrayList<>;
}
and when you are fetching your response you can call this by
//Inside your onCreate add the below code
mAdapter = new CommentsListAdapter (this);
content_newsCommentsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
content_newsCommentsRecyclerView.setAdapter(mAdapter);
//Call this inside your success of onResponse
commentsListAdapter.addAllItems(commentModel);
This will update the content of recyclerView and notify the changes made, do try this and let me know if you have any issue.

Before the API call initialize the Adapter
mLayoutManager = new LinearLayoutManager(getActivity());
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRVCommentList.setLayoutManager(mLayoutManager);
CommentsListAdapter commentsListAdapter= new CommentsListAdapter(this)
mRVCommentList.setAdapter(commentsListAdapter)
Then call the API
public void onResponse(Call<CommentResponse> call, Response<CommentResponse> response) {
if (response.body().getData() != null) {
List<CommentData> list= response.body().getData().getCommentData();
commentsListAdapter.setData(list);
}
}
Small changes in Adapter Class
private List<CommentData> commentData;
public CommentsListAdapter(Context context) {
this.context = context;
this.commentData = new ArrayList<>();
}
public void setData(List<CommentData> commentData) {
this.commentData= commentData;
notifyDataSetChanged();
}

Related

display data into recycler view using mutiple api request and one adapter

I'm using RecyclerView to display some data from an API response using one adapter
but i want to display data from more then one API response (so i need to make more then one get request)
this is my adapter :
private Context Context1;
private List<TraitementTicketModel> followuplist;
public FollowupAdapter(Context mContext, List<TraitementTicketModel> followuplist) {
this.Context1 = mContext;
this.followuplist = followuplist;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v;
LayoutInflater layoutInflater=LayoutInflater.from(Context1);
v=layoutInflater.inflate(R.layout.followupitem,parent,false);
return new MyViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
holder.users_id.setText(followuplist.get(position).getUsers_id());
holder.date.setText(followuplist.get(position).getDate());
holder.titre.setText(followuplist.get(position).getTitre());
holder.content.setText(html2text(followuplist.get(position).getContent()));
}
#Override
public int getItemCount() {
return followuplist.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder{
TextView users_id;
TextView date;
TextView content;
TextView titre;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
users_id=itemView.findViewById(R.id.user_id_followup);
date =itemView.findViewById(R.id.date_followup);
content=itemView.findViewById(R.id.contenu_followup);
titre=itemView.findViewById(R.id.titre_followup);
}
}
public static String html2text(String html) {
return Jsoup.parse(Jsoup.parse(html).text()).text();
}
and this is the code to display the data into the recyclerview
SharedPreferences sp = getApplicationContext().getSharedPreferences("tokenPref", Context.MODE_PRIVATE);
String sestoken = sp.getString("token", "");
SharedPreferences sp1 = getApplicationContext().getSharedPreferences("idPref", Context.MODE_PRIVATE);
String id = sp1.getString("id", "");
Retrofit retrofit = RetrofitInstance.getRetrofitInstance();
final Api api = retrofit.create(Api.class);
Call<List<TraitementTicketModel>> call = api.getfollowup(id, sestoken);
call.enqueue(new Callback<List<TraitementTicketModel>>() {
#Override
public void onResponse(Call<List<TraitementTicketModel>> call, Response<List<TraitementTicketModel>> response) {
if (!response.isSuccessful()) {
Toast.makeText(getApplicationContext(),"Something is wrong !! ",Toast.LENGTH_LONG).show();
Log.e("TAG", "onResponse: something is wrong");
}
List<TraitementTicketModel> followups = response.body();
for (TraitementTicketModel followup : followups) {
followuplist.add(followup);
}
followuplist.add(firstfollowup());
PutDataIntoRecyclerView(followuplist);
}
#Override
public void onFailure(Call<List<TraitementTicketModel>> call, Throwable t) {
}
});
private void PutDataIntoRecyclerView(List<TraitementTicketModel> followuplist) {
FollowupAdapter adapter =new FollowupAdapter(this,followuplist);
recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
}
so is it possible to display data into one recylerview using one adapter and more then one API request .
Edit :
this is the TraitementTicketModel class :
public class TraitementTicketModel {
private String users_id;
private String date;
private String content;
private String titre;
public TraitementTicketModel(String users_id, String date, String content, String titre) {
this.users_id = users_id;
this.date = date;
this.content = content;
this.titre = titre;
}
public String getTitre() {
return titre;
}
public String getUsers_id() { return users_id; }
public String getDate() { return date; }
public String getContent() {
return content;
}
}
when i try to sort the list by date before i put it in the adapter the data doesn't show .
in the adapter class :
public List addToList(List<TraitementTicketModel> list) {
this.followuplist.addAll(list);
notifyDataSetChanged();
Collections.sort(followuplist, new Comparator<TraitementTicketModel>() {
#Override
public int compare(TraitementTicketModel o1, TraitementTicketModel o2) {
return o2.getDate().compareTo(o1.getDate());
}
});
return this.followuplist;
}
in the main activity :
private void PutDataIntoRecyclerView(List<TraitementTicketModel> followuplist) {
Sort(followuplist);
if (adapter == null) {
adapter=new FollowupAdapter(this,followuplist);
recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
}else {
adapter=new FollowupAdapter(this,adapter.addToList(followuplist));
recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
}
}
private List Sort (List<TraitementTicketModel> datalist){
Collections.sort(datalist, new Comparator<TraitementTicketModel>() {
#Override
public int compare(TraitementTicketModel o1, TraitementTicketModel o2) {
return o2.getDate().compareTo(o1.getDate());
}
});
return datalist;
}
so is it possible to display data into one recylerview using one adapter and more then one api request
Yes it's possible if all API responses have the same data scheme as the adapter list.
You can add a method in the adapter that accumulates the current list:
public addToList(List<TraitementTicketModel> list) {
this.followuplist.addAll(list);
notifyDataSetChanged();
}
Then make the adapter as a class field, to only be instantiated for the first response, and reuse it in further responses:
FollowupAdapter mAdapter;
Then if the adapter not instantiated, instantiate it with the initial list, otherwise accumulate the list:
private void PutDataIntoRecyclerView(List<TraitementTicketModel> followuplist) {
if (mAdapter == null) {
mAdapter = new FollowupAdapter(this, followuplist);
recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
} else {
mAdapter.addToList(followuplist);
}
}

Unable to get data from Retrofit when used in for loop

I'm using MVVM and I'm trying to loop over a list of integers that has some IDs for movies genres, I'm using retrofit and I have a function that should return a list of movies based on the id, but it's working for some reason.
I tried to pass the id to the function and it worked, however when I tried to loop over a list of ids to get different results and put them in a RecyclerView it didn't work
MainActivity:
public class MainActivity extends AppCompatActivity {
RecyclerView mMainRecycler;
MoviesViewModel moviesViewModel;
MainRecyclerAdapter mainRecyclerAdapter;
Context context = this;
List<String> genresNames = new ArrayList<>();
List<Integer> genresIds = new ArrayList<>();
List<Movies.Result> listResults = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMainRecycler = findViewById(R.id.main_recycler);
mMainRecycler.setLayoutManager(new LinearLayoutManager(this));
moviesViewModel = new ViewModelProvider(this).get(MoviesViewModel.class);
moviesViewModel.getMoviesGenres();
moviesViewModel.mutableMoviesGenres.observe(this, new Observer<List<MoviesGenres.GenresBean>>() {
#Override
public void onChanged(List<MoviesGenres.GenresBean> genresBeans) {
for (int i = 0; i < genresBeans.size(); i++) {
genresIds.add(genresBeans.get(i).getId());
genresNames.add(genresBeans.get(i).getName());
}
mainRecyclerAdapter.addNames(genresNames);
}
});
for(int i = 0; i < genresIds.size(); i++) {
moviesViewModel.getMovieWithGenre(genresIds.get(i));
}
moviesViewModel.mutableMoviesWithGenre.observe(this, new Observer<List<Movies.Result>>() {
#Override
public void onChanged(List<Movies.Result> results) {
listResults.addAll(results);
mainRecyclerAdapter.addResults(listResults);
}
});
mMainRecycler.setLayoutManager(new LinearLayoutManager(this));
mainRecyclerAdapter = new MainRecyclerAdapter(context, genresNames, listResults);
mMainRecycler.setAdapter(mainRecyclerAdapter);
}
}
ViewModel:
public class MoviesViewModel extends ViewModel {
public MutableLiveData<List<Movies.Result>> mutablePopularMovies = new MutableLiveData<>();
public void getPopular(){
MoviesClient.getInstance().getPopular().enqueue(new Callback<Movies>() {
#Override
public void onResponse(#NonNull Call<Movies> call, #NonNull Response<Movies> response) {
if(response.isSuccessful() && response.body() != null) {
mutablePopularMovies.setValue(response.body().getResults());
}
}
#Override
public void onFailure(#NonNull Call<Movies> call, #NonNull Throwable t) {
t.printStackTrace();
}
});
}
public MutableLiveData<List<Movies.Result>> mutableMoviesWithGenre = new MutableLiveData<>();
public void getMovieWithGenre(int i){
MoviesClient.getInstance().getMoviesWithGenre(i).enqueue(new Callback<Movies>() {
#Override
public void onResponse(#NonNull Call<Movies> call, #NonNull Response<Movies> response) {
if(response.isSuccessful() && response.body() != null) {
mutableMoviesWithGenre.setValue(response.body().getResults());
}
}
#Override
public void onFailure(#NonNull Call<Movies> call, #NonNull Throwable t) {
t.printStackTrace();
}
});
}
public MutableLiveData<List<MoviesGenres.GenresBean>> mutableMoviesGenres = new MutableLiveData<>();
public void getMoviesGenres() {
MoviesClient.getInstance().getMoviesByGenre().enqueue(new Callback<MoviesGenres>() {
#Override
public void onResponse(#NonNull Call<MoviesGenres> call, #NonNull Response<MoviesGenres> response) {
if(response.isSuccessful() && response.body() != null) {
mutableMoviesGenres.setValue(response.body().getGenres());
}
}
#Override
public void onFailure(#NonNull Call<MoviesGenres> call, #NonNull Throwable t) {
t.printStackTrace();
}
});
}
}
MainRecyclerViewAdapter:
public class MainRecyclerAdapter extends RecyclerView.Adapter<MainRecyclerAdapter.MainViewHolder> {
private Context mContext;
private List<Movies.Result> moviesResults;
private List<String> moviesNames;
public MainRecyclerAdapter(Context context, List<String> moviesNames, List<Movies.Result> moviesResults) {
this.mContext = context;
this.moviesNames = moviesNames;
this.moviesResults = moviesResults;
}
#NonNull
#Override
public MainRecyclerAdapter.MainViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new MainViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.category_rv_item, parent, false));
}
#Override
public void onBindViewHolder(#NonNull final MainRecyclerAdapter.MainViewHolder holder, int position) {
holder.genresTv.setText(moviesNames.get(position));
setCatItemRecycler(holder.mRecycler, moviesResults);
}
#Override
public int getItemCount() {
return moviesNames.size();
}
public void addNames(List<String> moviesGenres) {
moviesNames.addAll(moviesGenres);
notifyDataSetChanged();
}
public void addResults(List<Movies.Result> listResults) {
moviesResults.addAll(listResults);
notifyDataSetChanged();
}
public class MainViewHolder extends RecyclerView.ViewHolder {
TextView genresTv;
RecyclerView mRecycler;
public MainViewHolder(#NonNull View itemView) {
super(itemView);
genresTv = itemView.findViewById(R.id.cat_title);
mRecycler = itemView.findViewById(R.id.item_recycler);
}
}
private void setCatItemRecycler(RecyclerView recyclerView, List<Movies.Result> moviesResults){
MovieAdapter movieAdapter = new MovieAdapter(mContext, moviesResults);
recyclerView.setLayoutManager(new LinearLayoutManager(mContext, RecyclerView.HORIZONTAL, false));
recyclerView.setAdapter(movieAdapter);
}
}
Chile (inner) RecyclerView:
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {
private List<Movies.Result> moviesResult;
private Context mContext;
public MovieAdapter(Context context, List<Movies.Result> moviesResult) {
this.moviesResult = moviesResult;
this.mContext = context;
}
#NonNull
#Override
public MovieAdapter.MovieViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new MovieViewHolder(LayoutInflater.from(mContext).inflate(R.layout.movie_item, parent, false));
}
#Override
public void onBindViewHolder(#NonNull MovieAdapter.MovieViewHolder holder, int position) {
String imageUrl = MoviesClient.IMAGE_URL + moviesResult.get(position).getPosterPath();
Glide.with(holder.mPosterImage.getContext()).load(imageUrl).into(holder.mPosterImage);
}
#Override
public int getItemCount() {
return moviesResult.size();
}
public class MovieViewHolder extends RecyclerView.ViewHolder {
ImageView mPosterImage;
public MovieViewHolder(#NonNull View itemView) {
super(itemView);
mPosterImage = itemView.findViewById(R.id.poster_image);
}
}
}
first of all you didn't share the ViewModel class, I thing you may have a problem with the observers, because your listResults will only be updated after the Retrofit returns the result, so when you call mMainRecycler.setAdapter(mainRecyclerAdapter) you are doing it with your empty list.
Another thing that may be causing this is because your data does not know it has returned, so when you use listResults.addAll(results), make sure that inside the adapter you call .notifyDataSetChanged()
A function like this should do the trick (inside the adapter):
public void updateGenres(List<Genres> genres){
listResults.addAll(genres);
this.notifyDataSetChanged();
}

How to clear the recyclerview / adapter in searchview for next search android

I am using retrofit to search the images and displaying. I am using SearchView to search and recyclerview and adapter to display the item.
It's working for the very first time. Second time when I search, it's displaying the same item. It's not clearing the view to display.
Here is my code:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getName();
ImagesViewModel newsViewModel;
List<Hits> newsArticles;
List<Hits> articleArrayList;
ImagesAdapter newsAdapter;
RecyclerView rvHeadline;
SearchView searchView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvHeadline = (RecyclerView) findViewById(R.id.rvNews);
searchView=(SearchView) findViewById(R.id.searchView);
searchView.setQueryHint("Search Images");
searchView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
searchView.setIconified(false);
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
callPixabayImages(query);
Toast.makeText(getBaseContext(), query, Toast.LENGTH_LONG).show();
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
callPixabayImages(newText);
Toast.makeText(getBaseContext(), newText, Toast.LENGTH_LONG).show();
return false;
}
});
articleArrayList = new ArrayList<>();
}
private void callPixabayImages(String query){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ImageApiInterface.PIXABAY_URL)
.addConverterFactory(GsonConverterFactory.create()) //Here we are using the GsonConverterFactory to directly convert json data to object
.build();
ImageApiInterface api = retrofit.create(ImageApiInterface.class);
Call<MyImages> call = api.getMovieDetails(query);
call.enqueue(new Callback<MyImages>() {
#Override
public void onResponse(Call<MyImages> call, Response<MyImages> response) {
Log.e("response","response"+response.code());
if(response.isSuccessful()){
Log.e("response","response"+response.code());
if((response.body() != null ? response.body().getHits() : null) !=null){
List<Hits> imagesList = response.body().getHits();
articleArrayList.addAll(imagesList);
newsAdapter.notifyDataSetChanged();
}
}
}
#Override
public void onFailure(Call<MyImages> call, Throwable t) {
System.out.println("Error: "+ t.getMessage());
Log.e("response","response"+t);
}
});
}
private void setupRecyclerView() {
if (newsAdapter == null) {
newsAdapter = new ImagesAdapter(MainActivity.this, articleArrayList);
rvHeadline.setLayoutManager(new LinearLayoutManager(this));
rvHeadline.setAdapter(newsAdapter);
rvHeadline.setItemAnimator(new DefaultItemAnimator());
rvHeadline.setNestedScrollingEnabled(true);
} else {
newsAdapter.notifyDataSetChanged();
}
}
Here where I need to reset to search the new item inorder to avoid to display the previous item. List is not clearing to display the new items.
Here is Adapter class:
public class ImagesAdapter extends RecyclerView.Adapter<ImagesAdapter.NewsViewHolder> {
private Context context;
private List<Hits> articles;
public ImagesAdapter(Context context, List<Hits> articles) {
this.context = context;
this.articles = articles;
}
#NonNull
#Override
public ImagesAdapter.NewsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.images_item, parent, false);
return new NewsViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ImagesAdapter.NewsViewHolder holder, final int position) {
String image = articles.get(position).getLargeImageURL();
Log.e("imagestest",image);
if (image != null) {
Glide.with(context) //passing context
.load(image) //passing your url to load image.
.dontAnimate()
.placeholder(context.getDrawable(R.drawable.image_not_available)) //this would be your default image (like default profile or logo etc). it would be loaded at initial time and it will replace with your loaded image once glide successfully load image using url.
//.centerCrop()//this method help to fit image into center of your ImageView
.into(holder.ivNews); //pass imageView reference to appear the image.*/
}
}
#Override
public int getItemCount() {
return articles.size();
}
class NewsViewHolder extends RecyclerView.ViewHolder {
ImageView ivNews;
private NewsViewHolder(#NonNull View itemView) {
super(itemView);
ivNews = itemView.findViewById(R.id.ivNews);
}
}
}
I am clueless where exactly I need to clear the view to fetch the newly searched item.
Trial 1: I tried to clear the list object articleArrayList but it throws crash that adapter size is null.
Clean the
articleArrayList
before loading the searched items again.... by executing this line articleArrayList.clear();
public void onResponse(Call<MyImages> call, Response<MyImages> response) {
Log.e("response","response"+response.code());
if(response.isSuccessful()){
Log.e("response","response"+response.code());
if((response.body() != null ? response.body().getHits() : null) !=null){
List<Hits> imagesList = response.body().getHits();
articleArrayList.clear();
articleArrayList.addAll(imagesList);
newsAdapter.notifyDataSetChanged();
}
}
You're adding generated imagesList to the articleArrayList, you should be replacing the values.
articleArrayList = imagesList;

How to filter data in a recycler view

I am fetching data from a server in a recycler view.In a layout file I have an EditText field on top and below it I have a recycler view.
I want to filter data based on what I have written in EditText field.
My problem is as I start typing in EditText field it shows no data in recycler and as I removes everything in EditText field it shows everything.
Why it is happening even if I have data present in recycler view with the same name I have entered in EditText field.
This is my code below:
Home.java
public class Home extends Fragment {
String myValue;
RecyclerView recycle;
ArrayList<LoadHomeBooks> list;
HomeBookAdapter adapter;
EditText search;
private static final String URL = "https://www.example.com";
public Home() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_home, container, false);
recycle = view.findViewById(R.id.recycle);
refresh = view.findViewById(R.id.refresh);
search = view.findViewById(R.id.search);
list = new ArrayList<>();
recycle.setHasFixedSize(true);
recycle.setLayoutManager(new LinearLayoutManager(getActivity()));
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(22, TimeUnit.SECONDS)
.readTimeout(22, TimeUnit.SECONDS)
.writeTimeout(22, TimeUnit.SECONDS)
.build();
RequestBody formBody = new FormBody.Builder().add("city", myValue).build();
Request request = new Request.Builder().url(URL).post(formBody).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
try {
JSONArray jsonArray = new JSONArray(response.body().string());
for (int i = jsonArray.length() - 1; i > -1; i--) {
JSONObject object = jsonArray.getJSONObject(i);
String str1 = object.getString("Book_name");
LoadHomeBooks model = new LoadHomeBooks(str1);
list.add(model);
}
adapter = new HomeBookAdapter(list, getActivity());
recycle.setAdapter(adapter);
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
#Override
public void onFailure(Call call, final IOException e) {
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
TastyToast.makeText(getActivity(), e.getMessage(), TastyToast.LENGTH_LONG, TastyToast.ERROR).show();
}
});
}
}
});
search.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
ArrayList<LoadHomeBooks> filterBooks = new ArrayList<>();
for(LoadHomeBooks books: list){
String name = books.getbName().toLowerCase();
if(name.contains(s)){
filterBooks.add(books);
}
adapter.setFilter(filterBooks);
}
}
});
return view;
}
}
HomeBookAdapter.java
public class HomeBookAdapter extends RecyclerView.Adapter<HomeBookAdapter.ViewHolder> {
ArrayList<LoadHomeBooks> list;
Context context;
public HomeBookAdapter(ArrayList<LoadHomeBooks> list,Context context){
this.list = list;
this.context = context;
}
#NonNull
#Override
public HomeBookAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.home_book_layout,viewGroup,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull HomeBookAdapter.ViewHolder viewHolder, int i) {
LoadHomeBooks model = list.get(i);
viewHolder.homeBookName.setText(model.getbName());
}
#Override
public int getItemCount() {
return list.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView homeBookName;
public ViewHolder(#NonNull View itemView) {
super(itemView);
homeBookName = itemView.findViewById(R.id.homeBookName);
}
}
public void setFilter(ArrayList<LoadHomeBooks> filterBooks){
list = new ArrayList<>();
list.addAll(filterBooks);
notifyDataSetChanged();
}
}
LoadHomeBooks.java
public class LoadHomeBooks {
String bName;
public LoadHomeBooks(){
}
public LoadHomeBooks(String bName){
this.bName = bName;
}
public String getbName() {
return bName;
}
public void setbName(String bName) {
this.bName = bName;
}
}
Someone please let me know what I am doing wrong. Any help would be appreciated.
THANKS
Move this code outside the for Loop
adapter.setFilter(filterBooks);
Because adapter is calling set Filter after each iteration.
Also I would request you to move network request to Activity instead of Fragment using interface.
for(LoadHomeBooks books: list){
String name = books.getbName().toLowerCase();
if(name.contains(s)){
filterBooks.add(books);
}
adapter.setFilter(filterBooks); //Place this line outside forloop
}

Viewmodel shows empty recyclerview when activity rotated

I am pretty new to the Android architecture components and have been trying out room for data storage from my server. Problem is no data is being shown on the recycler view IMMEDIATELY. There's a searchview(no logic implemented just there) right above my recyclerview and when I click searchview for input, the recyclerview shows all the data which was supposed to be shown earlier.
RestaurantsAdapter:
public class RestaurantsAdapter extends RecyclerView.Adapter<RestaurantsAdapter.MyViewHolder> {
private List<Restaurant> data;
private Context context;
private LayoutInflater layoutInflater;
private final Random r = new Random();
public RestaurantsAdapter(Context context) {
this.data = new ArrayList<>();
this.context = context;
this.layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public RestaurantsAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_restaurant, parent, false);
return new RestaurantsAdapter.MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RestaurantsAdapter.MyViewHolder holder, int position) {
holder.rName.setText(data.get(position).getName());
}
public void setData(List<Restaurant> newData) {
if (data != null) {
RestaurantDiffCallback restaurantDiffCallback = new RestaurantDiffCallback(data, newData);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(restaurantDiffCallback);
data.clear();
data.addAll(newData);
diffResult.dispatchUpdatesTo(this);
} else {
// first initialization
data = newData;
}
}
#Override
public int getItemCount() {
return data.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView rName;
public MyViewHolder(View itemView) {
super(itemView);
rName = (TextView) itemView.findViewById(R.id.restaurant_name);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
}
}
class RestaurantDiffCallback extends DiffUtil.Callback {
private final List<Restaurant> oldRestaurants, newRestaurants;
public RestaurantDiffCallback(List<Restaurant> oldPosts, List<Restaurant> newPosts) {
this.oldRestaurants = oldPosts;
this.newRestaurants = newPosts;
}
#Override
public int getOldListSize() {
return oldRestaurants.size();
}
#Override
public int getNewListSize() {
return newRestaurants.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldRestaurants.get(oldItemPosition).getIdentifier().equals(newRestaurants.get(newItemPosition).getIdentifier());
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldRestaurants.get(oldItemPosition).equals(newRestaurants.get(newItemPosition));
}
}}
MainActivity:
public class MainActivity extends AppCompatActivity {
private RestaurantsAdapter restaurantsAdapter;
private RestaurantViewModel restaurantViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
restaurantsAdapter = new RestaurantsAdapter(this);
restaurantViewModel = ViewModelProviders.of(this).get(RestaurantViewModel.class);
restaurantViewModel.getAllRestaurants().observe(this, restaurants -> restaurantsAdapter.setData(restaurants));
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(restaurantsAdapter);
}}
ViewModel:
public class RestaurantViewModel extends AndroidViewModel {
private RestaurantDao restaurantDao;
private ExecutorService executorService;
private ApiInterface webService;
public RestaurantViewModel(#NonNull Application application) {
super(application);
restaurantDao = RestaurantsDatabase.getInstance(application).restaurantDao();
executorService = Executors.newSingleThreadExecutor();
webService = ApiClient.getApiClient().create(ApiInterface.class);
}
LiveData<List<Restaurant>> getAllRestaurants() {
refreshUser();
return restaurantDao.findAll();
}
private void refreshUser() {
executorService.execute(() -> {
int numOfRestaurants = restaurantDao.totalRestaurants();
if (numOfRestaurants < 30) {
Call<RestaurantsModel> call = webService.getRestaurants();
call.enqueue(new Callback<RestaurantsModel>() {
#Override
public void onResponse(#NonNull Call<RestaurantsModel> call, #NonNull Response<RestaurantsModel> response) {
restaurantDao.saveAll(response.body().getData().getData());
}
#Override
public void onFailure(#NonNull Call<RestaurantsModel> call, #NonNull Throwable t) {
}
});
}
});
}}
If you don't use the DiffUtil with its diffResult.dispatchUpdatesTo(this); you should do notifyDataSetChanged(). In your case, in RestaurantsAdapter.setData add one line:
// first initialization
data = newData;
notifyDataSetChanged();
You have an issue in your setData method:
public void setData(List<Restaurant> newData) {
if (data != null) {
RestaurantDiffCallback restaurantDiffCallback = new RestaurantDiffCallback(data, newData);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(restaurantDiffCallback);
data.clear();
data.addAll(newData);
diffResult.dispatchUpdatesTo(this);
} else {
// first initialization
data = newData;
}
}
When your newData is null your change the data source of your adapter, but you don't call notifyDataSetChanged.
This way the data that you are seeing on the screen will not be updated.
So in order to fix it:
public void setData(List<Restaurant> newData) {
if (data != null) {
RestaurantDiffCallback restaurantDiffCallback = new RestaurantDiffCallback(data, newData);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(restaurantDiffCallback);
data.clear();
data.addAll(newData);
diffResult.dispatchUpdatesTo(this);
} else {
// first initialization
data = newData;
notifyDataSetChanged();
}
}
Another thing, if not a very good practice setting your adapter dataset has null. So my suggestion is to set your data as an empty list instead of null:
data = new ArrayList<>();
The issue: you're using ViewModels incorrectly. You are only returning data from the ViewModel, but never saving data in the ViewModel
Start by reading how ViewModels work here: https://developer.android.com/topic/libraries/architecture/viewmodel

Categories