I fetch data with Retrofit using. I have 10 results per page. When I scroll to the last result, there is no further possibility to view the туче results. So, I need to update the adapter. but nothing comes of it. Here is part of my code:
retrofit:
private final RestClient restClient;
private List<Data> newData;
private DataAdapter dataAdapter;
public void fetchData() {
Call<Data> callItems = restClient.searchData();
callItems.enqueue(new Callback<Data>() {
#Override
public void onResponse(#NonNull Call<Data> call, #NonNull Response<Data> response) {
newData = response.body().getPersons();
personAdapter.addPersons(newData );
}
#Override
public void onFailure(#NonNull Call<Data> call, #NonNull Throwable t) {
}
});
}
here is my method from adpter class where I add new items:
private List<Person> persons = new ArrayList<>();
..................
public void addIPersons(List<Person> newPerson) {
this.iersons= newIPeroon
persons.addAll(newIPerson;
notifyDataSetChanged();
}
You should use mutable list and should not overwrite it.
But the better way is to use listadapter
Just update your data source and call notifyDatasetChanged() on your RecyclerViewAdapter.
After adding to your list like this:
persons.add(newIPerson);
call:
dataAdapter.notifyDatasetChanged();
Related
hello i have two problems regarding live data with view model and navigation component first one is when i go from fragment A with live data to fragment B and then from B to A the data in my list gets duplicated,
the other problem is one i re call viewModel.loadList() in my fragment after making and event to filter the data also gets duplicated
here is my view model
public class HomeViewModel extends ViewModel {
MutableLiveData<ArrayList<HomeResponseModel>> homeLiveData = new MutableLiveData<>();
ArrayList<HomeResponseModel> homeList = new ArrayList<>();
public MutableLiveData<ArrayList<HomeResponseModel>> geHomeList(HomeRequestModel homeRequestModel, Context context, ApiInterface apiInterface, LottieAnimationView lottieAnimationView) {
if (homeLiveData == null) {
homeLiveData = new MutableLiveData<ArrayList<HomeResponseModel>>();
loadHomeList(homeRequestModel);
}
return homeLiveData;
}
public void loadHomeList(HomeRequestModel homeRequestModel) {
Call<List<HomeResponseModel>> call = apiInterface.getHomeList(homeRequestModel, );
call.enqueue(new Callback<List<HomeResponseModel>>() {
#Override
public void onResponse(Call<List<HomeResponseModel>> call, Response<List<HomeResponseModel>> response) {
if (response.isSuccessful()) {
homeList.addAll(response.body());
homeLiveData.setValue(homeList);
}
}
#Override
public void onFailure(Call<List<HomeResponseModel>> call, Throwable t) {
}
});
}
my observer in onCreateView
viewModel.geHomeList(homeRequestModel).observe(getViewLifecycleOwner(), new Observer<ArrayList<HomeResponseModel>>() {
#Override
public void onChanged(ArrayList<HomeResponseModel> homeResponse) {
homeResponseModels.addAll(homeResponse);
homeAdapter.notifyDataSetChanged();
}
});
}
how i recall load method after a filter event
viewModel.loadHomeList(homeRequestModel);
Clear the list before adding the new models:
viewModel.geHomeList(homeRequestModel).observe(getViewLifecycleOwner(), new Observer<ArrayList<HomeResponseModel>>() {
#Override
public void onChanged(ArrayList<HomeResponseModel> homeResponse) {
homeResponseModels.clear();
homeResponseModels.addAll(homeResponse);
homeAdapter.notifyDataSetChanged();
}
});
}
Or, even better:
In case your adapter holds a List or HomeResponseModel you could create a method to update it:
public update(List<HomeResponseModel> homeResponse) {
this.homeResponseModels = homeResponse;
notifydatasetchanged();
}
and then change the observe method to call it:
viewModel.geHomeList(homeRequestModel).observe(getViewLifecycleOwner(), new Observer<ArrayList<HomeResponseModel>>() {
#Override
public void onChanged(ArrayList<HomeResponseModel> homeResponse) {
homeAdapter.update(homeResponse);
}
});
}
Besides that, in your ViewModel in the loadHomeList method in the onResponse callback you could assign the data received to the liveData:
if (response.isSuccessful()) {
homeLiveData.setValue(response.body());
}
no need to save it in the homeList var, you can get rid of that var. Otherwise, perform homeList.clear(); before adding to it all the received data to avoid duplicates.
I'm making a CommentFragment. I have a RecyclerView to list comments and a Edittext to write comment. However when I add a comment it's sent to server but RecyclerView isn't updated. I use notifydatasetchanged to update. Code:
private void getComments(){
Call<List<CommentsModel>> call=restApiClass.getComments(post_id);
call.enqueue(new Callback<List<CommentsModel>>() {
#Override
public void onResponse(Call<List<CommentsModel>> call, Response<List<CommentsModel>> response) {
if(response.isSuccessful()){
list=response.body();
if(commentsAdapter==null) {
commentsAdapter = new CommentsAdapter(list, getContext());
}
else{
commentsAdapter.notifyDataSetChanged();
}
recyclerView.setAdapter(commentsAdapter);
}
}
#Override
public void onFailure(Call<List<CommentsModel>> call, Throwable t) {
}
});
}
I call this method when I click to sendCommentTextView:
sendCommentTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//............send comment codes.........
getComments();
}
});
check this correct answer: notifyDataSetChanged example.
I think you should follow there the answer to make the adapter work as expected with notifyDataSetChanged(). Shortly: you have to update the list inside the adapter and then notify to it that the list has been updated, so it will render again on ui (recyclerview).
PS: after notifyDataSetChanged function, you don't have to set the adapter again on the recyclerview if already assigned.
Here you updated the list when Retrofit brings new data back, but actually you didn't feed your adapter with this data; you just called commentsAdapter.notifyDataSetChanged();, but you need first to add a method like updateList(List<CommentsModel> list) in your adapter, and call it with updateList(list); before using commentsAdapter.notifyDataSetChanged();
So, add updateList(List<CommentsModel> list) into your adapter to update its list internally like Below:
class CommentsAdapter extends RecyclerView.Adapter<CommentsAdapter.ViewHolder> {
...
List<CommentsModel> mList;
updateList(List<CommentsModel> list) {
this.mList.clear();
this.mList.addAll(list);
}
}
And then the change in your code could be something like:
call.enqueue(new Callback<List<CommentsModel>>() {
#Override
public void onResponse(Call<List<CommentsModel>> call, Response<List<CommentsModel>> response) {
if(response.isSuccessful()){
list=response.body();
if(commentsAdapter==null) {
commentsAdapter = new CommentsAdapter(list, getContext());
}
else{
commentsAdapter.updateList(list);
commentsAdapter.notifyDataSetChanged();
}
recyclerView.setAdapter(commentsAdapter);
}
}
#Override
public void onFailure(Call<List<CommentsModel>> call, Throwable t) {
}
});
Update
Yes it's working but recylerview return to first position and list
reload. But I want to load only last comment. Other comments must stay
same. So how can I do that ?
To just update the last comment in your list, then add a new method into your adapter that takes only that comment, and add it to the list like:
class CommentsAdapter extends RecyclerView.Adapter<CommentsAdapter.ViewHolder> {
...
List<CommentsModel> mList;
addComment(CommentsModel comment) {
this.mList.add(comment);
notifyItemInserted(mList.size()-1);
}
}
Then when the data comes in back by Retrofit:
call.enqueue(new Callback<List<CommentsModel>>() {
#Override
public void onResponse(Call<List<CommentsModel>> call, Response<List<CommentsModel>> response) {
if(response.isSuccessful()){
list = response.body();
if(commentsAdapter == null) {
commentsAdapter = new CommentsAdapter(list, getContext());
}
else{
// updating the adapter list with the last comment
commentsAdapter.addComment((CommentsModel) list.get(list.size()-1));
}
recyclerView.setAdapter(commentsAdapter);
}
}
#Override
public void onFailure(Call<List<CommentsModel>> call, Throwable t) {
}
});
Im working on mvvm design but OnResponse is not saving the data in List. İts returning the emtpy List array. I cant reach the valued List. I realy dont know where is the incorrect piece of code. Here is the code.Help please.
public class RetroClass {
private static final String BASE_URL="--";
private List<ProductModel> productList=new ArrayList<>();
public static Retrofit getRetroInstance(){
return new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();
}
public static APIService getAPIService(){
return getRetroInstance().create(APIService.class);
}
public List<ProductModel> getProducts(){
APIService apiService=getAPIService();
apiService.getProducts().enqueue(new Callback<List<ProductModel>>() {
#Override
public void onResponse(Call<List<ProductModel>> call, Response<List<ProductModel>> response) {
productList.addAll(response.body());
for (int k=0;k<productList.size();k++) {
Log.d("onResponse: ", productList.get(k).getOrderName());//im getting the value here
}
}
#Override
public void onFailure(Call<List<ProductModel>> call, Throwable t) {
Log.d("onFailure: ",t.getMessage());
}
});
return productList;//but this is empty
}
}
Here is my view model.
public class ProductsVievModal extends ViewModel {
List<ProductModel> productList;
LiveData<List<ProductModel>> liveproductList;
RetroClass apiClass=new RetroClass();
public List<ProductModel> getProducts(){
productList=apiClass.getProducts();
for (int k=0;k<productList.size();k++) {
Log.d("onResponse: ", productList.get(k).getOrderName());
}
return productList;
}
}
.enqueue is asynchronously sending the request and notify callback of its response. It is asynchronous. onResponse() must complete before you return the product list.
I suspect the return productList; is executed before the onResponse() returned its value. Can you check by putting a log before return productList; to see which line is executed first?
Your data IS saved, but the moment your code needs it the .enqueue is probably not done retrieving the data yet. so it shows as empty
Easiest way to fix this is by having your follow up code in the onResponse so your basicly stuck waiting untill it either retrieved the information or failed
so something like this
public void onResponse(Call<List<ProductModel>> call, Response<List<ProductModel>> response) {
productList.addAll(response.body());
nextFunction();
}
}
#Override
public void onFailure(Call<List<ProductModel>> call, Throwable t) {
Log.d("onFailure: ",t.getMessage());
failFunction();
}
```
I use RxJava in my Android project, I want to retrieve all entities User from a table using RxJava asynchronously and return the list List to an adapter myAdapter(Context context, List<User> users).
But now I can only have Single<User>, how can I get the list directly and put it into my adapter ?
My code:
// here vm.getUserList() should return a List<User>
MyAdapter adapter = new MyAdapter(context, vm.getUserList());
...
And in my vm:
public Single<List<User>> getUserList() {
return Single.fromCallable(() -> myDatabaseRepository.getUsers());
}
You only need to call the users method and handle the success/error in the corresponding method.
getUserList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSuccess(new Consumer<List<User>>() {
#Override
public void accept(List<User> users) throws Exception {
// fill adapter
MyAdapter adapter = new MyAdapter(context, users);
}
})
.doOnError(new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
// handle error
}
});
Yes, this is the normal way of retrieving the data from the database/network. You expose from the repository the Observables/Singles, then you subscribe to them and receive the data in the success/error methods to show in the UI to the user.
private void fetchUser() {
('function returning Single<List<User>>').subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccess, this::onFail);
}
private void onSuccess(List<User> users) {
//handle user model here
}
private void onFail(Throwable t) {
//handle error here
}
I want to delete the recyclerview item using retrofit or volley network library. Please post the code or example?
Firstable you do not have to remove the items on the RecyclerView. The main idea is provide the List of item, set it in the Adapter and load again the RecyclerView on the UI. If you have a RestCall using Retrofit all you have to do is:
Create an Update method and call it every time you need to show the changes on the UI. This method will do the RetrofitCall and it will give the List of items you need to pass to the adapter using a custom listener with the return onSuccess.
private RecyclerAdapter updateRecycler() {
getItemsFromApi(new RestApiListener() {
#Override
public void onSuccess(List<Items> items) {
recyclerAdapter = new RecyclerAdapter(this, items);
recyclerView.setAdapter(recyclerAdapter);
}
#Override
public void onError(String message) {
showError(message);
}
});
}
Here is how to do the CallBack in Retrofit with the listener
public Callback<Items> getItemsFromApi(final RestApiListener listener) {
return new Callback<Items>() {
#Override
public void onResponse(Call<Items> call, Response<Items> response) {
switch (response.code()) {
case 200:
listener.onSuccess(response.body().getItems());
break;
default:
listener.onError("Error " + response.message());
break;
}
}
#Override
public void onFailure(Call<Items> call, Throwable t) {
listener.onError(t.getMessage().toString());
}
};
}
public interface RestApiListener {
void onSuccess(List<Items> items);
void onError(String s);
}
Also here is an example code.