How to load data into recyclerView - java

In my application i want get some data from server and show into recyclerView. For application architecture i used MVP
I wrote below codes, but after loaded data from server, not show any data into recyclerView!
I used debug mode and show me data in this break point
public void add(List<DataItem> list) {
list.addAll(list);
notifyDataSetChanged();
}
but not show me data into recyclerView!
My Activity codes :
public class ListFragment extends Fragment implements ActiveTestsContract.View {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_tester_active_tests, container, false);
//Initialize
init();
//User token log
if (!App.isEmptyString(App.getPrefs("JWT")) || !App.getPrefs("JWT").equals(ConstKeys.EMPTY)) {
userToken = App.getPrefs("JWT");
}
//Load data
getData();
return view;
}
#Override
public void updateTestsList(Data data, int page) {
testerDashboard_emptyLay.setVisibility(View.GONE);
activeTests_pullToLoader.setVisibility(View.VISIBLE);
//Get lasted page
if (page == data.getLastPage()) {
isHasLoadedAll = true;
activeTests_pullToLoader.setComplete();
}
adapter.add(data.getData());
//Complete items
isLoading = false;
nextPage = page + 1;
activeTests_pullToLoader.setComplete();
}
#Override
public void init() {
context = getActivity();
handler = new Handler(Looper.getMainLooper());
testsPresenter = new ActiveTestsPresenter(this, 3);
testerDashboard_loader = view.findViewById(R.id.testerDashboard_loader);
activeTests_pullToLoader = view.findViewById(R.id.activeTests_pullToLoader);
testerDashboard_emptyLay = view.findViewById(R.id.testerDashboard_emptyLay);
emptyLayout_editProfileBtn = view.findViewById(R.id.emptyLayout_editProfileBtn);
layoutManager = new LinearLayoutManager(context);
recyclerView = activeTests_pullToLoader.getRecyclerView();
//Adapter
adapter = new TesterActiveRecyclerAdapter(activeModel, context);
//Init recycler and adapter
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
activeTests_pullToLoader.setColorSchemeResources(R.color.colorPrimary);
}
private void getData() {
activeTests_pullToLoader.isLoadMoreEnabled(true);
activeTests_pullToLoader.setPullCallback(new PullCallback() {
#Override
public void onLoadMore() {
isLoading = true;
//Call api
testsPresenter.testsListResponse(App.recipesApi, userToken, nextPage);
}
#Override
public void onRefresh() {
adapter.clear();
isHasLoadedAll = false;
isLoading = true;
//Call api
testsPresenter.testsListResponse(App.recipesApi, userToken, 1);
}
#Override
public boolean isLoading() {
return isLoading;
}
#Override
public boolean hasLoadedAllItems() {
return isHasLoadedAll;
}
});
activeTests_pullToLoader.initLoad();
}
Adapter codes:
public class TesterActiveRecyclerAdapter extends RecyclerView.Adapter<TesterActiveRecyclerAdapter.ViewHolder> {
private List<DataItem> list;
private Context context;
public TesterActiveRecyclerAdapter(List<DataItem> list, Context context) {
this.list = list;
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_tester_test_list, parent, false);
return new ViewHolder(view);
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
DataItem item = list.get(position);
//Name
holder.txt_name.setText(item.getTitle());
//Conditions
if (!App.isEmptyString(item.getOs()) && !App.isEmptyString(item.getType()) && !App.isEmptyString(item.getBrowser())) {
holder.txt_condition.setText(item.getType() + " | " + item.getOs() + " | " + item.getBrowser());
}
//Button actions
holder.setState(item.getState(), position);
//Price
holder.rowTests_priceTxt.setText(item.getPrice() + " Dollar");
//Animate items
Animation animation = AnimationUtils.loadAnimation(App.context,
(position > list.size() - 1) ? R.anim.down_from_top : R.anim.up_from_bottom);
holder.itemView.startAnimation(animation);
Toast.makeText(context, ""+list.get(0).getId(), Toast.LENGTH_SHORT).show();
}
#Override
public int getItemCount() {
return list.size();
}
public void add(List<DataItem> list) {
list.addAll(list);
notifyDataSetChanged();
}
public void clear() {
list.clear();
notifyDataSetChanged();
}
class ViewHolder extends RecyclerView.ViewHolder {
private ViewGroup root;
private TextView txt_name, txt_value, txt_condition, rowTests_priceTxt;
private RoundTextView rowTests_button;
ViewHolder(View view) {
super(view);
root = (ViewGroup) view;
txt_name = view.findViewById(R.id.txtTestListTitle);
txt_value = view.findViewById(R.id.txtTestListSublist);
txt_condition = view.findViewById(R.id.txtTestListSublist2);
rowTests_priceTxt = view.findViewById(R.id.rowTests_priceTxt);
rowTests_button = view.findViewById(R.id.rowTests_button);
}
}
How can i fix it?

You are adding data again in the same list passed in the parameter. Try to replace the below code
public void add(List<DataItem> list) {
list.addAll(list);
notifyDataSetChanged();
}
with
public void add(List<DataItem> list) {
this.list.addAll(list);
notifyDataSetChanged();
}

Clear the current list
Add the data to the adapter's list.
public void add(List<DataItem> list) {
clear();
this.list.addAll(list);
notifyDataSetChanged();
}
Your approach didn't work because you were updating the very list that you were passing in the function and not the adapter's list.

First add all data into list & than notify your adapter , after this set your adapter to recyclerview.
list.addAll(data)
adapter.notifyDataSetChanged()
val layoutManager = LinearLayoutManager(activity)
recyclerview.isNestedScrollingEnabled = false
recyclerview.layoutManager = layoutManager
recyclerview.itemAnimator = DefaultItemAnimator()
recyclerview.adapter = mAdapter

Related

empty recycler view in Fragment

I have an activity that contains 2 fragments
in TriFragment.java the user entres an input and click the button Sortto sort the numbers , the input entred will be sent to the second fragment StepsFragmentto display mystepsList in a Recyclerview.
i have cheked and found that mystepsListis empty even before the user enters the data (on creating the activity) so the recyclerview is not showing anything
mainactivity
public class Sorting_activity extends AppCompatActivity implements StepsFragment.OnFragmentInteractionListener,TriFragment.TriFragmentListner{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sorting_activity);
TriFragment tri = new TriFragment();
FragmentManager manager=getSupportFragmentManager();
manager.beginTransaction()
.replace(R.id.triLayout,tri ,tri.getTag())
.commit();
StepsFragment steps= new StepsFragment();
manager.beginTransaction()
.replace(R.id.stepsLayout,steps ,steps.getTag())
.commit();
}
#Override
public void onFragmentInteraction(Uri uri) { }
#Override
public void onInputsent(String input) {
StepsFragment.setTextView(input);
}
}
StepsFragment
public class StepsFragment extends Fragment {
private OnFragmentInteractionListener mListener;
private static TextView inputEntred ;
private RecyclerView RV ;
private Handler mhandler = new Handler();
public StepsFragment() {
// Required empty public constructor
}
public static void setTextView(String textentered){
inputEntred.setText(textentered);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view =inflater.inflate(R.layout.fragment_steps, container, false);
inputEntred =view.findViewById(R.id.inputEntred);
String[] numberList = inputEntred.getText().toString().split(",");
final Integer[] numbers = new Integer[numberList.length];
if(numberList.length !=0){
// converting the strig array to an array of integers
for (int i = 0; i < numberList.length; i++) {
try {
numbers[i] = Integer.parseInt(numberList[i]);
}
catch (NumberFormatException e){
System.out.println("error is"+e);
}
}
SelectionSort m1 = new SelectionSort();
ArrayList<String> mystepsList = m1.steps(numbers);// returns the steps numbers
if(mystepsList.isEmpty()){
System.out.println("empty list");
}else {
for (int i = 0; i < mystepsList.size(); i++)
{
System.out.println(mystepsList.get(i));
}
}
RV =view.findViewById(R.id.RV);
final StepListAdapter adapter = new StepListAdapter() ;
RV.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
RV.setLayoutManager(llm);
adapter.setList(mystepsList);
}
return view;
}
#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");
}
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
adapter
public class StepListAdapter extends RecyclerView.Adapter<StepListAdapter.StepListHolder> {
private ArrayList<String> etapes_list = new ArrayList<String>();
#NonNull
#Override
public StepListHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new StepListHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.log_item,parent,false));
}
#Override
public void onBindViewHolder(#NonNull StepListHolder holder, int position) {
holder.etapes.setText(etapes_list.get(holder.getAdapterPosition()));
}
#Override
public int getItemCount() {
return etapes_list.size();
}
public void setList(ArrayList<String> etapes_list ){
this.etapes_list= etapes_list;
notifyDataSetChanged();
}
#Override
public long getItemId(int position) {
return (null != etapes_list ? etapes_list.size() : 0);
}
public class StepListHolder extends RecyclerView.ViewHolder {
TextView etapes ;
public StepListHolder(#NonNull View itemView) {
super(itemView);
etapes =itemView.findViewById(R.id.txt_log);
}
}
}
Because you see you are setting a recyclerView in onCreateView method of Stepsfragment, this means your computation is done when fragment is created and not when you actully press the sort button.
modify your fragment this way and your recyclerView will works fine.
package com.example;
public class StepsFragment extends Fragment {
private OnFragmentInteractionListener mListener;
private static TextView inputEntred ;
private RecyclerView RV ;
private Handler mhandler = new Handler();
public StepsFragment() {
// Required empty public constructor
}
public static void setTextView(String textentered){
inputEntred.setText(textentered);
//Set Your recyclerView Here.
String[] numberList = inputEntred.getText().toString().split(",");
final Integer[] numbers = new Integer[numberList.length];
if(numberList.length !=0){
// converting the strig array to an array of integers
for (int i = 0; i < numberList.length; i++) {
try {
numbers[i] = Integer.parseInt(numberList[i]);
}
catch (NumberFormatException e){
System.out.println("error is"+e);
}
}
SelectionSort m1 = new SelectionSort();
ArrayList<String> mystepsList = m1.steps(numbers);// returns the steps numbers
if(mystepsList.isEmpty()){
System.out.println("empty list");
}else {
for (int i = 0; i < mystepsList.size(); i++)
{
System.out.println(mystepsList.get(i));
}
}
final StepListAdapter adapter = new StepListAdapter() ;
RV.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
RV.setLayoutManager(llm);
adapter.setList(mystepsList);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view =inflater.inflate(R.layout.fragment_steps, container, false);
inputEntred =view.findViewById(R.id.inputEntred);
RV =view.findViewById(R.id.RV);
return view;
}
#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");
}
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
and yes try to avoid making method static for setting data in Fragment.

Custom Card View Adapter : notify data set changed not working

Im setting up a custom card-view slider adapter and on startup the card view loads up fine but after items is added/replaced in the array list and the notify adapter method is called, no changes occur to the content.
I have already tried adding the items directly from the adapter via a public method and the item gets added however, it is always placed at the last position.
This is my adapter :
static class UploadImageAdapter extends CardSliderAdapter<ImageObject>
{
private Context context;
private ArrayList<ImageObject> arrayList;
private OnUploadButtonClick onUploadButtonClick;
public interface OnUploadButtonClick{
void upload(int position);
}
UploadImageAdapter(Context context, ArrayList<ImageObject> items) {
super(items);
this.context = context;
this.arrayList = items;
}
static class ViewHolder{
ImageView image;
ImageButton addImage;
Button removeImage;
View layout;
}
#Override
public void bindView(int position, #NotNull View itemContentView, ImageObject imageObject) {
final ViewHolder holder = new ViewHolder();
holder.image = itemContentView.findViewById(R.id.card_image);
holder.addImage = itemContentView.findViewById(R.id.upload_image_btn);
holder.removeImage = itemContentView.findViewById(R.id.card_remove_btn);
holder.layout = itemContentView.findViewById(R.id.card_image_layout);
if (imageObject.hasImage) {
Glide.with(context)
.load(Uri.parse(imageObject.getImage_path()))
.placeholder(R.color.offsetWhiteBackground)
.centerCrop()
.transition(withCrossFade())
.into(holder.image);
holder.addImage.setVisibility(View.GONE);
holder.layout.setVisibility(View.VISIBLE);
holder.removeImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
else {
holder.layout.setVisibility(View.GONE);
holder.addImage.setVisibility(View.VISIBLE);
holder.addImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onUploadButtonClick.upload(position);
}
});
}
}
#Override
public ImageObject getItem(int position) {
return arrayList.get(position);
}
void setOnUploadButtonClick(OnUploadButtonClick onUploadButtonClick){
this.onUploadButtonClick = onUploadButtonClick;
}
#Override
public int getItemContentLayout(int position) {
//TODO return the item layout of every position
return R.layout.upload_image_card;
}
}
And below is where i instantiate :
private void setCardSlider() {
upload = new ArrayList<>();
for(int i = 0; i < 3; i++){
upload.add(new ImageObject(i,false,""));
}
adapter = new UploadImageAdapter(getContext(), upload);
CardSliderViewPager cardSliderViewPager = rootView.findViewById(R.id.image_upload_slider);
cardSliderViewPager.setAdapter(adapter);
uploadDialog = new UploadDialog(getActivity(), getContext());
adapter.setOnUploadButtonClick((int position) -> {
uploadDialog.callUploadDialog();
mPosition = position;
});
}
public void uploadImage(){
uploadDialog.callUploadDialog();
}
public void setImage(Uri uri){
upload.get(mPosition).setHasImage(true);
upload.get(mPosition).setImage_path(uri.toString());
adapter.notifyDataSetChanged();
}
I would suggest using notifyItemChange instead. There you use a position as a parameter and it doesn't need to redraw everything.

Changing the UI in the onComplete method of the OnCompleteListener for a Task

I'm using a Cloud Firestore database to populate a RecyclerView in an Android app. I'm getting the data by using a Task in the onAttach method of a Fragment. I need to be able to update the UI, the RecyclerView with data from the Cloud Firestore.
I populated the RecyclerView with dummy data in the onAttach method of the Fragment and that worked, but when I put the same loop that inserts dummy data in the onComplete method of a OnCompleteListener that's used in the Task that pulls data from the Cloud Firestore, the RecyclerView doesn't update and the list stays blank. I need to do it there to eventually insert data from the Cloud Firestore.
Within the Fragment. The data coming back from the Firestore database is correct and I see all of the Log statements in the onComplete method in the Logcat.
ChatListFragment:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_chat_list, container, false);
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
if (mColumnCount <= 1) {
recyclerView.setLayoutManager(new LinearLayoutManager(context));
} else {
recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
}
chatRecyclerViewAdapter = new ChatRecyclerViewAdapter(ChatList.ITEMS, mListener);
recyclerView.setAdapter(chatRecyclerViewAdapter);
}
return view;
}
...
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnListFragmentInteractionListener) {
mListener = (OnListFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnListFragmentInteractionListener");
}
Log.d(LOG_TAG, "activity attached, creating Firestore instance");
FirebaseFirestore db = FirebaseFirestore.getInstance();
//Worked, but doesn't in OnCompleteListener
/*for (int i = 1; i <= 10; i++) {
ChatList.addItem(ChatList.createDummyItem(i));
}*/
Task<QuerySnapshot> task = db.collection("chats").get();
task.addOnCompleteListener(getActivity(), new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(LOG_TAG, "ID = " + document.getId() + " => " + document.getData());
ChatListMessage chatListMessage = document.toObject(ChatListMessage.class);
for (int i = 1; i <= 10; i++) {
Log.d(LOG_TAG, "adding message");
ChatList.addItem(ChatList.createDummyItem(i));
}
Log.d(LOG_TAG, "ChatListMessage members " + chatListMessage.getLastMessage());
}
} else {
Log.w(LOG_TAG, "Error getting documents.", task.getException());
}
}
});
}
Within the ChatList class
public static void addItem(ChatListItem item) {
ITEMS.add(item);
ITEM_MAP.put(item.userId, item);
}
public static ChatListItem createDummyItem(int position) {
return new ChatListItem(String.valueOf(position), R.drawable.profile_circle, makeDetails(position),
new Timestamp(System.currentTimeMillis()));
}
public static class ChatListItem {
public final String userId;
public final int pictureUrl;
public final String lastMessage;
public final Timestamp timeStamp;
public ChatListItem(String userId, int pictureUrl, String details, Timestamp timeStamp) {
this.userId = userId;
this.pictureUrl = pictureUrl;
this.lastMessage = details;
this.timeStamp = timeStamp;
}
#Override
public String toString() {
return userId;
}
public Timestamp getTimeStamp() {
return timeStamp;
}
public String getTLastMessage() {
return lastMessage;
}
}
The custom RecyclerViewAdapter
public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerViewAdapter.ViewHolder> {
private final List<ChatListItem> mValues;
private final OnListFragmentInteractionListener mListener;
public ChatRecyclerViewAdapter(List<ChatListItem> items, OnListFragmentInteractionListener listener) {
mValues = items;
mListener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_chat, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.contactImageView.setImageResource(mValues.get(position).pictureUrl);
holder.contactImageView.setScaleType(ImageView.ScaleType.FIT_XY);
holder.mContentView.setText(mValues.get(position).lastMessage);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onListFragmentInteraction(holder.mItem);
}
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
public final ImageView contactImageView;
public final TextView messageMembersTextView;
public final TextView mContentView;
public final TextView timestampView;
public ChatListItem mItem;
public ViewHolder(View view) {
super(view);
mView = view;
messageMembersTextView = view.findViewById(R.id.message_members);
contactImageView = view.findViewById(R.id.contact_imageView);
mContentView = view.findViewById(R.id.content_textView);
timestampView = view.findViewById(R.id.timestamp_textView);
}
#Override
public String toString() {
return super.toString() + " '" + mContentView.getText() + "'";
}
}
}
How can I get the UI to be updated with the onComplete method of the OnCompleteListener?
For this, chatRecyclerViewAdapter.notifyDataSetChanged() needs to be called in the onComplete method of the OnCompleteListener. I forgot to do this outside of the listener since it looks like the list items are pulled in after the onAttach method is called.

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

I am trying to scroll to the same position in recyclerview when the device is rotated - although the position gets saved does not get displayed right

I am trying to scroll to the same position in my Recyclerview when a user rotates the device. I am saving the Position here:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
int index = llm.findFirstCompletelyVisibleItemPosition();//llm is my layoutmanager
savedInstanceState.putInt("Pos", index);
super.onSaveInstanceState(savedInstanceState);
}
And restore it here:
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mPosRev = savedInstanceState.getInt("Pos");
Log.d(TAG, "onSaveInstanceState: "+mPosRev);
llm.scrollToPositionWithOffset(mPosRev,10);
}
I can see by the logs, that the Position is stored correctly, but not scrolled to. I already tried smoothScrollToPosition as well as scrollToPosition.
This is my Adapter
private final Review mLock = new Review();
private List<Review> mObjects;
public ReviewAdapter( List<Review> objects) {
mObjects = objects;
}
public void add(Review object) {
synchronized (mLock) {
mObjects.add(object);
}
notifyDataSetChanged();
}
public void add(ReviewDetail object) {
synchronized (mLock) {
try {
Log.d(TAG, "add: "+object.getContent());
mObjects.add(new Review(object));
} catch (JSONException e) {
e.printStackTrace();
}
}
notifyDataSetChanged();
}
public void clear() {
synchronized (mLock) {
mObjects.clear();
}
notifyDataSetChanged();
}
#Override
public ReviewViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType){
Context context=viewGroup.getContext();
int layoutIdForListItem=R.layout.item_movie_review;
LayoutInflater inflater=LayoutInflater.from(context);
View view=inflater.inflate(layoutIdForListItem, viewGroup, false);
ReviewViewHolder viewHolder= new ReviewViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(ReviewViewHolder holder, int position) {
holder.bind(position);
}
#Override
public int getItemCount() {
if (null == mObjects) return 0;
return mObjects.size();
}
class ReviewViewHolder extends RecyclerView.ViewHolder {
TextView Author;
TextView Txt;
public ReviewViewHolder(View itemView) {
super(itemView);
Author = (TextView) itemView.findViewById(R.id.author);
Txt = (TextView) itemView.findViewById(R.id.txt);
}
void bind(int listIndex) {
Author.setText(mObjects.get(listIndex).getAuthor());
Txt.setText(mObjects.get(listIndex).getContent());
}
}
}
And in here I initalize my Recyclerview:
recList = (RecyclerView) findViewById(R.id.cardList);
recList.setHasFixedSize(true);
llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
mReviewAdapter = new ReviewAdapter(new ArrayList<Review>());
recList.setAdapter(mReviewAdapter);
Any ideas on what could cause this behavior?

Categories