Getting a null object exception when using an interface - android

I have created a shopping cart via recyclerview. Each viewholder also has a + and - button to add or remove quantity. This functionality is done in the adapter and I have to notify the parent fragment of the updated total amount. This last bit is done via an interface. The problem is, I am getting the following error:
Process: com.ecomm.market, PID: 6630
java.lang.NullPointerException: Attempt to invoke interface method 'void com.ecomm.market.SelectionAdapter$OnUpdateCartListener.onUpdateCart(int)' on a null object reference
at com.ecomm.market.SelectionAdapter$ViewHolder$1.onClick(SelectionAdapter.java:103)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
The selected item and corresponding quantity are stored in a hashmap that I convert to an array to populate the recycler view cart. This is what the relevant part of my main fragment MenuFragment looks like:
// Using the adapter interface to add items to cart and adding up total amount
menuItemAdapter.setOnAddToCartListener(new MenuItemAdapter.OnAddToCartListener() {
#Override
public void onAddToCart(final HashMap selectionItemsHashMap) {
setupSelectionRecycler(menuView);
totalAmount = mapToArray(selectionItemsHashMap);
selectionAdapter = new SelectionAdapter(selectionItemArrayList, selectionItemsHashMap, totalAmount);
selectionRecycler.setAdapter(selectionAdapter);
cartItemsHashmap = selectionItemsHashMap;
selectionAdapter.setUpdateCartListener(new SelectionAdapter.OnUpdateCartListener() {
#Override
public void onUpdateCart(int updatedTotalAmount) {
String stringTotalAmount = Integer.toString(updatedTotalAmount);
Log.d(TAG, "received total:" +stringTotalAmount);
tvTotalAmount.setText("$"+ stringTotalAmount);
totalAmount = updatedTotalAmount;
}
});
}
});
}
And here is an excerpt from my adapter SelectionAdapter:
public class SelectionAdapter extends RecyclerView.Adapter<SelectionAdapter.ViewHolder> {
private static final String TAG = SelectionAdapter.class.getSimpleName();
private ArrayList<SelectionItem> selectionItemArrayList = new ArrayList<>();
public HashMap<String, Integer> selectionItemsHashMap = new HashMap<String, Integer>();
public int totalAmount;
private OnUpdateCartListener updateCartListener;
public interface OnUpdateCartListener {
void onUpdateCart(int totalAmount);
}
public void setUpdateCartListener(OnUpdateCartListener updateCartListener) {
this.updateCartListener = updateCartListener;
}
public SelectionAdapter(ArrayList<SelectionItem> selectionItemArrayList, HashMap<String, Integer> selectionItemsHashMap, int currentTotalAmount) {
this.selectionItemArrayList = selectionItemArrayList;
this.selectionItemsHashMap = selectionItemsHashMap;
this.totalAmount = currentTotalAmount;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.selection_card, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int position) {
viewHolder.tvSelectItem.setText(selectionItemArrayList.get(position).getSelectionName());
String quantity = Integer.toString(selectionItemArrayList.get(position).getSelectionQuantity());
viewHolder.tvDishQuantity.setText(quantity);
}
#Override
public int getItemCount() {
return selectionItemArrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView tvSelectItem, tvDishQuantity;
ImageView addQuantity, subtractQuantity, deleteItem;
public ViewHolder(#NonNull View itemView) {
super(itemView);
tvSelectItem = itemView.findViewById(R.id.dish_selection);
tvDishQuantity = itemView.findViewById(R.id.dish_quantity);
addQuantity = itemView.findViewById(R.id.button_add);
subtractQuantity = itemView.findViewById(R.id.button_subtract);
deleteItem = itemView.findViewById(R.id.delete_item);
addQuantity.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Find the name of the menu item
int position = getAdapterPosition();
String itemName = selectionItemArrayList.get(position).getSelectionName();
// Increase quantity
int currentQuantity = selectionItemsHashMap.get(itemName);
currentQuantity += 1;
// Update the hashmap with the new quantity
selectionItemsHashMap.put(itemName,currentQuantity);
//Update total amount
for (Object name: selectionItemsHashMap.keySet()) {
String key = (String) name;
if (key.equals(selectionItemArrayList.get(position).getSelectionName())) {
int eachPrice = Integer.parseInt(selectionItemArrayList.get(position).getSelectionPrice());
totalAmount += eachPrice;
}
}
// Display the new quantity
String stringCurrentQuantity = Integer.toString(currentQuantity);
tvDishQuantity.setText(stringCurrentQuantity);
//updateCartListener.onUpdateCart(totalAmount);
}
});
}
EDIT
A couple of points:
- When the cart is initially filled, I use this same code and it works fine.
- The problem occurs once the user returns to the cart and I have to repopulate it from a bundle. Everything works fine, except when the user goes straight to the cart and attempts to alter the quantity from there.

Pass interface implementation into the constructor of Adapter as follows :
public SelectionAdapter(ArrayList<SelectionItem> selectionItemArrayList, HashMap<String, Integer> selectionItemsHashMap, int currentTotalAmount,UpdateCartListener updateCartListener ) {
this.selectionItemArrayList = selectionItemArrayList;
this.selectionItemsHashMap = selectionItemsHashMap;
this.totalAmount = currentTotalAmount;
this.updateCartListener = updateCartListener
}
This will provide an implementation of your interface on the initialization of your adapter so it will be not null when you are using it for update cart.

So you try to access the listener inside the ViewHolder which is probably the cause of the crash, what the standard way is setting the OnClickListener inside your adapter
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int position) {
viewHolder.tvSelectItem.setText(selectionItemArrayList.get(position).getSelectionName());
String quantity = Integer.toString(selectionItemArrayList.get(position).getSelectionQuantity());
viewHolder.tvDishQuantity.setText(quantity);
viewHolder.addQuantity.setOnClickListener(new View.OnClickListener() {
...
}
}

You need to instantiate your updateCartListener begfore you use it. Do updateCartListener = new WhatverClassThatImplementsYourInterafce() before you use it or make your method to override Default if you are using Java 8 using the new FunctionalInterface in java.util.Function.

I guess you forget assignment the updateCartListener in your adapter.
SelectionAdapter(selectionItemArrayList, selectionItemsHashMap, totalAmount);
cartItemsHashmap = selectionItemsHashMap;
selectionAdapter.setUpdateCartListener(new SelectionAdapter.OnUpdateCartListener() {
#Override
public void onUpdateCart(int updatedTotalAmount) {
String stringTotalAmount = Integer.toString(updatedTotalAmount);
Log.d(TAG, "received total:" +stringTotalAmount);
tvTotalAmount.setText("$"+ stringTotalAmount);
totalAmount = updatedTotalAmount;
}
});
selectionRecycler.setAdapter(selectionAdapter);

Related

Why am i getting a nullpointerexception when I try to setAdapter?

I have a simple adapter that uses 2 String based array lists. For some reason I am getting a NullPointerException even though I am sure the information is being passed by.
My adapter:
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.ViewHolder> {
private ArrayList<String> names = new ArrayList<>();
private ArrayList<String> details = new ArrayList<>();
private Context mContext;
public MovieAdapter(ArrayList<String> names, ArrayList<String> details, Context mContext) {
this.names = names;
this.details = details;
this.mContext = mContext;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.detail_view, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.name.setText(names.get(position));
holder.detail.setText(details.get(position));
}
#Override
public int getItemCount() {
return names.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
private TextView name;
private TextView detail;
private ConstraintLayout cl;
public ViewHolder(#NonNull View itemView) {
super(itemView);
name = itemView.findViewById(R.id.name);
detail = itemView.findViewById(R.id.detial);
cl = itemView.findViewById(R.id.layout);
}
}
}
My REST call and recylerView initialization activity(added the REST call in case the problem is lying there):
// ArrayList<String> namesArray = new ArrayList<>(Arrays.asList("Rated:", "Released:", "Runtime:", "Genre:", , "Writer:", "Actors:", "Plot:", "Language:", "Country:"));
ArrayList<String> namesArray = new ArrayList<>();
namesArray.add("Rated:");
namesArray.add("Released:");
namesArray.add("Runtime:");
namesArray.add("Genre:");
namesArray.add("Director:");
namesArray.add("Writer:");
namesArray.add("Actors:");
namesArray.add("Plot");
namesArray.add("Language:");
namesArray.add("Country");
trazi.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Call<Movie> call;
movieApi = ApiClient.getClient().create(MovieApi.class);
if (paramYear.getText().toString().matches("")) {
call = movieApi.getDetails(key, paramName.getText().toString(), null);
} else {
call = movieApi.getDetails(key, paramName.getText().toString(), Integer.parseInt(paramYear.getText().toString()));
}
call.enqueue(new Callback<Movie>() {
#Override
public void onResponse(Call<Movie> call, Response<Movie> response) {
Toast.makeText(getApplication().getApplicationContext(), " valja", Toast.LENGTH_LONG).show();
movie = response.body();
// ArrayList<String> details = new ArrayList<>(Arrays.asList(movie.getRated(), movie.getReleased(), movie.getRuntime(), movie.getGenre(), movie.getDirector(), movie.getWriter(), movie.getActors(), movie.getPlot(), movie.getLanguage(), movie.getCountry()));
ArrayList<String> details = new ArrayList<>();
details.add(movie.getRated());
details.add(movie.getReleased());
details.add(movie.getRuntime());
details.add(movie.getGenre());
details.add(movie.getDirector());
details.add(movie.getWriter());
details.add(movie.getActors());
details.add(movie.getPlot());
details.add(movie.getLanguage());
details.add(movie.getCountry());
Picasso.get().load(movie.getPoster()).fit().centerInside().into(image);
image.setVisibility(View.VISIBLE);
title.setText(movie.getTitle());
title.setVisibility(View.VISIBLE);
recyclerView = findViewById(R.id.layout);
movieAdapter = new MovieAdapter(namesArray, details, getApplication().getApplicationContext());
recyclerView.setAdapter(movieAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplication().getApplicationContext()));
}
So
Picasso.get().load(movie.getPoster()).fit().centerInside().into(image);
and
title.setText(movie.getTitle());
work perfectly so the information is passed to the adapter and I tried to initialize array lists on two different ways just in case. Still no success. Any ideas on where the problem is?
Edit:
My error log:
2020-05-24 17:20:22.600 27595-27595/com.example.cetvrtizadatak E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.cetvrtizadatak, PID: 27595
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.recyclerview.widget.RecyclerView.setAdapter(androidx.recyclerview.widget.RecyclerView$Adapter)' on a null object reference
at com.example.cetvrtizadatak.MainActivity$1$1.onResponse(MainActivity.java:115)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1.lambda$onResponse$0$DefaultCallAdapterFactory$ExecutorCallbackCall$1(DefaultCallAdapterFactory.java:81)
at retrofit2.-$$Lambda$DefaultCallAdapterFactory$ExecutorCallbackCall$1$3wC8FyV4pyjrzrYL5U0mlYiviZw.run(Unknown Source:6)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6702)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)
Your NullPointerException is happening when you try to access a member of you recyclerView object, meaning that its value is probably null.
Looking at your code I can presume that the id you're passing is not of yout recyclerView but for an layout object.
Double check if in your layout xml file the id given to the recyclerView is the one you're referencing on you call to findViewById.

Show only certain items in recycleview according to condition

I have a recycleview showing a list of audio files fetched from my audios.json file hosted on my server. i have a model class with a getter method getLanguage() to see the audio language. I would like to show only audio files of users preference in recycle view. Say for example, if user wants only english and russian i would like to show only list of russian and english. How can we achieve this? Right now the entire list is displayed.
public class AudioAdapter extends RecyclerView.Adapter<AudioAdapter.HomeDataHolder> {
int currentPlayingPosition = -1;
Context context;
ItemClickListener itemClickListener;
List<Output> wikiList;
public AudioAdapter(List<Output> wikiList, Context context) {
this.wikiList = wikiList;
this.context = context;
}
#NonNull
#Override
public HomeDataHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.audio_row_layout,viewGroup,false);
HomeDataHolder mh = new HomeDataHolder(view);
return mh;
}
#Override
public void onBindViewHolder(#NonNull final HomeDataHolder homeDataHolder, int i) {
String desc = wikiList.get(i).getLanguage() + " • " + wikiList.get(i).getType();
homeDataHolder.tvTitle.setText(wikiList.get(i).getTitle());
homeDataHolder.tvotherinfo.setText(desc);
homeDataHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (itemClickListener != null)
itemClickListener.onClick(view,homeDataHolder.getAdapterPosition());
}
});
homeDataHolder.rippleLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (itemClickListener != null)
itemClickListener.onClick(view,homeDataHolder.getAdapterPosition());
}
});
}
#Override
public int getItemCount() {
return wikiList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
public void setClickListener(ItemClickListener itemClickListener) { // Method for setting clicklistner interface
this.itemClickListener = itemClickListener;
}
public class HomeDataHolder extends RecyclerView.ViewHolder {
TextView tvTitle,tvotherinfo;
MaterialRippleLayout rippleLayout;
public HomeDataHolder(View v) {
super(v);
this.tvTitle = v.findViewById(R.id.title);
this.tvotherinfo = v.findViewById(R.id.audioDesc);
this.rippleLayout = v.findViewById(R.id.ripple);
}
}
}
The general idea for this should be:
you have one list with all items
you have filter rules selected by the user
You filter items from number 1, to see which ones match the constraints and store this in another list.
Then the recycler view only shows the items of the list from number 3.
This means that recycler view's getItemCount would return the size of the filtered list, not the whole list.
Instead of passing the wikiList as it is, filter it then send it:
Lets say that you filled up the wikiList, before passing it to the adapter, filter it like this:
In the activity that you initialize the adapter in:
public class YourActivity extends ............{
........
........
//your filled list
private List<Output> wikiList;
//filtered list
private List<Output> filteredList= new ArrayList<Output>();
//filters
private List<String> filters = new ArrayList<String>();
//lets say the user chooses the languages "english" and "russian" after a button click or anything (you can add as many as you want)
filters.add("english");
filters.add("russian");
//now filter the original list
for(int i = 0 ; i<wikiList.size() ; i++){
Output item = wikiList.get(i);
if(filters.contains(item.getLanguage())){
filteredList.add(item);
}
}
//now create your adapter and pass the filteredList instead of the wikiList
AudioAdapter adapter = new AudioAdapter(filteredList , this);
//set the adapter to your recyclerview........
......
.....
......
}
I use above "english" and "russian" for language. I don't know how they are set in your response, maybe you use "en" for "english" so be careful.

Firebase add new child when an item on adapter is non-zero

Hello so I have a function in an app where you will add a data from the adapter if the quantity is non-zero and it will be saved to the firebase realtime database. What i wanna do is if the other item in adapter is non-zero it will add a child to the database but instead firebase is just replacing the item instead of adding a new child what should i do?
here is the code
public class UsualFragRViewAdapter extends RecyclerView.Adapter <UsualFragRViewAdapter.ViewHolder> {
private List<FragmentsUsualModels> items;
private Context context;
public UsualFragRViewAdapter( Context context,List<FragmentsUsualModels> items ) {
this.context = context;
this.items = items;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_usual_array, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
final FragmentsUsualModels arrayitems = items.get(position);
holder.itemName.setText(arrayitems.getItemName());
holder.price.setText(String.valueOf("$ " +arrayitems.getPrice()));
holder.quantity.setNumber(arrayitems.getQuantity());
holder.card.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
holder.quantity.setOnValueChangeListener(new ElegantNumberButton.OnValueChangeListener() { //all items are located in its positions
#Override
public void onValueChange(ElegantNumberButton view, int oldValue, int newValue) { //Need to pass all non-zero items as chatview
arrayitems.setQuantity(String.valueOf(newValue));
if (newValue !=0){
String datavalue = holder.itemName.getText().toString();
String dataprice = holder.price.getText().toString();
String dataquantity = holder.quantity.getNumber().toString();
DatabaseReference data = FirebaseDatabase.getInstance().getReference("itemdata");
data.child("dataname").setValue(datavalue);
data.child("dataprice").setValue(dataprice);
data.child("dataquantity").setValue(dataquantity);
Log.d(TAG, "the new value of this data is: " +dataquantity);
Log.d(TAG, "the itemname of this position is: "+datavalue);
Log.d(TAG, "the price of this item in this position is: " +dataprice);
}
Log.d(TAG, "user changed the quantity in this position to " +arrayitems);
}
});
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
CardView card;
ImageView image;
TextView itemName;
TextView price;
ElegantNumberButton quantity;
Button donebtn;
public ViewHolder(#NonNull View itemView) {
super(itemView);
card = itemView.findViewById(R.id.ucard);
image = itemView.findViewById(R.id.uimage);
itemName = itemView.findViewById(R.id.uitemName);
price = itemView.findViewById(R.id.uprice);
quantity = itemView.findViewById(R.id.uquantity);
donebtn = itemView.findViewById(R.id.udonebtn);
}
}
}
If you want to generate a new child node under a location, call push() on that DatabaseReference. So to create a new child node under itemdata:
DatabaseReference data = FirebaseDatabase.getInstance().getReference("itemdata");
DatabaseReference newData = data.push();
Now you can write the data to this new location as:
newData.child("dataname").setValue(datavalue);
newData.child("dataprice").setValue(dataprice);
newData.child("dataquantity").setValue(dataquantity);
One additional change to consider is the reducing the number of writes. Your current code does a separate setValue() call for each property. This works, but it means that any listeners will get called three times, once for each property.
While this may be what you want, it is quite common to want these writes to appear as one operation. If that is the case, you can perform a single setValue() with:
Map<String,Object> values = new HashMap<>();
values.put("dataname", datavalue);
values.put("dataprice", dataprice);
values.put("dataquantity", dataquantity);
newData.setValue(values);
The end result will be exactly the same as before, but now with a single write operation.
You should use push() to create unique id for database item
dataBase.child(/*CHILD*/).push().setValue(dataValue);

Removing element from adapter throws Index out of bound exception

I have two recycler views. The first recyclerview is basically a list of data where I can choose an item and its quantity and I am storing this chosen item data into a map. The second one is the list of the selected data. which I am generating from getting the values() from the map. The second one also has similar viewholder and I can change the quantity there also. One the quantity reaches zero I remove the item from the list and try to notifydatasetChanged().
The problem is the removing of an item from the second list is not working properly and the app crashes with error
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder
I am using a listener interface on my first Recycler so that when the quantity is changed and the item is added to the map. The adapter of the second recycler is notified of the changes. below is the code i am using to update the second recycler view.
public void updateList(){
mMap = ((UserMainActivity)getActivity()).getItemMap();
inputs.clear();
// adapter.notifyDataSetChanged();
adapter = new MyCartAdapter(inputs,getContext());
cartList.setAdapter(adapter);
for(AllItems t:mMap.values()) {
inputs.add(t);
}
adapter.notifyDataSetChanged();
}
Below is my second recycler view's adapter. Where I am changing the quantities of the selected items.
public class MyCartAdapter extends RecyclerView.Adapter<MyCartAdapter.MyCartViewHolder>{
private List<AllItems> listItems1;
private Context context;
private Typeface typeface;
public MyCartAdapter(List<AllItems> listItems1, Context context) {
this.listItems1 = listItems1;
this.context = context;
}
#NonNull
#Override
public MyCartAdapter.MyCartViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cart_items_layout, parent, false);
return new MyCartViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull final MyCartAdapter.MyCartViewHolder holder, final int position) {
final AllItems orderItem = listItems1.get(position);
holder.setProductImage(orderItem.getImageUrl(),context);
holder.name.setText(orderItem.getName());
String price = String.valueOf(orderItem.getPrice());
holder.price.setText(price);
final HashMap<String, AllItems> items = ((UserMainActivity)context).getItemMap();
holder.counter.setText(orderItem.getQuantity());
holder.add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String quantity = String.valueOf(holder.counter.getText());
int count = Integer.parseInt(quantity)+1;
holder.counter.setText(String.valueOf(count));
String url = orderItem.getImageUrl();
AllItems newitem = new AllItems(orderItem.getName(),orderItem.getComname(),url, String.valueOf(count),orderItem.getWeight,orderItem.getPrice());
((UserMainActivity)context).addItem(orderitemname,newitem);
// notifyItemChanged(position);
}
});
//counter text iitem.textview showing the quantity of the selected item . integer count returns the value of counter text below i am checking if its zero than it simply sets the value to zero and else reduce it and update the map.
holder.minus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String counterText = String.valueOf(holder.counter.getText());
int count = Integer.parseInt(counterText);
if (count==0){
holder.counter.setText("0");
}
if (count==1){
holder.counter.setText("0");
AllItems item = items.get(orderItem.getName());
if (item!=null){
String orderit = orderItem.getName();
((UserMainActivity)context).removeItem(orderit);
// here i am removing the value from the list which throws the exception
listItems1.remove(position);
notifyItemRemoved(position);
}
}
if (count>1){
String quantity = String.valueOf(count-1);
holder.counter.setText(quantity);
String orderitemname = orderItem.getName();
String url = orderItem.getImageUrl();
String weight = "100";
long weightl = Long.parseLong(weight);
AllItems newitem = new AllItems(orderItem.getName(),orderItem.getComname(),url, quantity,weight,orderItem.getPrice());
((UserMainActivity)context).addItem(orderitemname,newitem);
// listItems1.set(position, newitem);
// notifyItemChanged(position);
}
}
});
}
#Override
public int getItemCount() {
return listItems1.size();
}
public class MyCartViewHolder extends RecyclerView.ViewHolder {
public TextView name,price,count,comname;
public TextView weight;
LinearLayout add,minus;
TextView counter;
public MyCartViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.ProName);
price = (TextView) itemView.findViewById(R.id.proPrice);
weight = (TextView) itemView.findViewById(R.id.ProWeight);
counter = (TextView) itemView.findViewById(R.id.counter);
add = (LinearLayout) itemView.findViewById(R.id.addLin);
minus= (LinearLayout) itemView.findViewById(R.id.minusLin);
}
public void setProductImage(final String thumb_image, final Context ctx){
productImage = (ImageView) itemView.findViewById(R.id.ProImage);
Picasso.with(ctx).setIndicatorsEnabled(false);
Picasso.with(ctx)
.load(thumb_image)
.networkPolicy(NetworkPolicy.OFFLINE)
.placeholder(R.drawable.basket_b).into(productImage, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(ctx).load(thumb_image).placeholder(R.drawable.basket).into(productImage);
}
});
}
public void setComname(String name){
comname = (TextView)itemView.findViewById(R.id.proComName);
comname.setText(name);
}
}
}
This jumps out at me:
listItems1.remove(position);
notifyItemChanged(position);
The notifyItemChanged() method exists to tell the adapter that the data at the given position has changed, and that the ViewHolder should be re-bound. This is not what you're doing; you're removing an item.
Probably your app is crashing because you're removing the last item in your data set (e.g. position 10) and then telling the adapter that the item at position 10 has changed... but now the maximum position in your data set is 9.
Instead, you should use the notifyItemRemoved() method.
listItems1.remove(position);
notifyItemRemoved(position);

Compare Old Data with New Data in Recyclerview

I am new in Android Development and working on a project where I need to call an API after every one second, in that API there is field "Amount"(dBID) which keeps on changing, so I need to update the latest Amount (dBID) in recyclerview.
In order to do so, I have called this API in a service after every interval of one second.
The data is Showing Properly no Issue.
But for Now I need to perform some action on the Old Amount and New Amount.
Action Required : I need to compare the old value (dBID) with the New Value (dBID).
If the New Value is greater then I need to change the Text Color of Amount (dBID) to BLUE.
If the New Value is smaller then I need to change the Text Color of Amount (dBID) to RED.
Tried to achieve this by storing the old data in a Variable and then Comparing it to the new Value.
Issue : This logic is working fine until there are 5 or less Items in recyclerview as soon as the sixth item is added the same logic does not work.
Help me if anyone knows how I can achieve this.
For Example you can refer an App Vertexfx : Quotes Tab.
Below is the Code which I Tried.
Adapter class of the RecyclerView:
public class QuoteAdapter extends RecyclerView.Adapter <QuoteAdapter.MyViewHolder>{
Context context;
List<QuoteData> data;
public QuoteAdapter(Context context,List<QuoteData> data)
{
this.data = data;
this.context = context;
}
class MyViewHolder extends RecyclerView.ViewHolder{
TextView time,symbol,sellmax,selllow,buymax,buylow,buy,sell,spread,lowtext,hightext;
LinearLayout layout,layoutbid,layoutask;
float currentbid,lastbid,currentask,lastask;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
time = itemView.findViewById(R.id.TVTime);
symbol = itemView.findViewById(R.id.TVSymbol);
sellmax = itemView.findViewById(R.id.TVSELLMAX);
selllow = itemView.findViewById(R.id.TVSELLLOW);
buymax = itemView.findViewById(R.id.TVBUYMAX);
buylow = itemView.findViewById(R.id.TVBUYHIGH);
buy = itemView.findViewById(R.id.TVBUY);
sell = itemView.findViewById(R.id.TVSELL);
spread = itemView.findViewById(R.id.TVSpread1);
lowtext = itemView.findViewById(R.id.low);
hightext = itemView.findViewById(R.id.high);
layout = itemView.findViewById(R.id.layout);
layoutbid = itemView.findViewById(R.id.LLBid);
layoutask = itemView.findViewById(R.id.LLAsk);
currentbid = 0;
lastbid = 0;
currentask = 0;
lastask = 0;
}
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.quotelist,viewGroup,false);
return new MyViewHolder(view);
}
#SuppressLint("ResourceAsColor")
#Override
public void onBindViewHolder(#NonNull final MyViewHolder myViewHolder, final int i) {
final QuoteData data1 = data.get(i);
myViewHolder.time.setText(data1.dLut);
myViewHolder.symbol.setText(data1.dSymbol);
myViewHolder.sellmax.setText(data1.dBid); //Bid
myViewHolder.selllow.setText(data1.dLow);
myViewHolder.buymax.setText(data1.dAsk); //ask
myViewHolder.buylow.setText(data1.dHigh);
myViewHolder.currentbid = Float.parseFloat((data1.dBid));
myViewHolder.currentask = Float.parseFloat((data1.dAsk));
if (myViewHolder.currentbid > myViewHolder.lastbid)
{
myViewHolder.sellmax.setTextColor(Color.BLUE);
}
if (myViewHolder.currentbid < myViewHolder.lastbid)
{
myViewHolder.sellmax.setTextColor(Color.RED);
}
myViewHolder.lastbid = myViewHolder.currentbid;
myViewHolder.lastask = myViewHolder.currentask;
}
});
}
I suggest you take a look at those classes from the Android SDK:
DiffUtil
AsyncListDiffer
ItemAnimator
DiffUtil
DiffUtil is designed to compare existing and new recycler view items and fires appropriate events. You need to pass a callback that can tell if two items are the same and if their content has changed.
AsyncListDiffer
It wraps the DiffUtil and executes it's logic asynchronously, giving better performance.
ItemAnimator
The ItemAnimator for a given RecyclerView is called by default when change events are fired on it's items. You can provide an implementation of the animateChange method to change your color accordingly.
For Future reference I have resolved the above mentioned issue using the below code.
Defined two ArrayList of String in Adapter
public class QuoteAdapter extends RecyclerView.Adapter <QuoteAdapter.MyViewHolder>{
Context context;
List<QuoteData> data;
List<String> olddatabid = new ArrayList<String>();
List<String> newdatabid = new ArrayList<String>();
List<String> olddataask = new ArrayList<String>();
List<String> newdataask = new ArrayList<String>();
class MyViewHolder extends RecyclerView.ViewHolder{
TextView time,symbol,sellmax,selllow,buymax,buylow,buy,sell,spread,lowtext,hightext;
LinearLayout layout,layoutbid,layoutask;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
time = itemView.findViewById(R.id.TVTime);
symbol = itemView.findViewById(R.id.TVSymbol);
sellmax = itemView.findViewById(R.id.TVSELLMAX);
selllow = itemView.findViewById(R.id.TVSELLLOW);
buymax = itemView.findViewById(R.id.TVBUYMAX);
buylow = itemView.findViewById(R.id.TVBUYHIGH);
buy = itemView.findViewById(R.id.TVBUY);
sell = itemView.findViewById(R.id.TVSELL);
spread = itemView.findViewById(R.id.TVSpread1);
lowtext = itemView.findViewById(R.id.low);
hightext = itemView.findViewById(R.id.high);
layout = itemView.findViewById(R.id.layout);
layoutbid = itemView.findViewById(R.id.LLBid);
layoutask = itemView.findViewById(R.id.LLAsk);
}
}
public QuoteAdapter(Context context,List<QuoteData> data)
{
this.data = data;
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.quotelist,viewGroup,false);
return new MyViewHolder(view);
}
#SuppressLint("ResourceAsColor")
#Override
public void onBindViewHolder(#NonNull final MyViewHolder myViewHolder, final int i) {
final QuoteData data1 = data.get(i);
myViewHolder.time.setText(data1.dLut);
myViewHolder.symbol.setText(data1.dSymbol);
myViewHolder.sellmax.setText(data1.dBid); //Bid
myViewHolder.selllow.setText(data1.dLow);
myViewHolder.buymax.setText(data1.dAsk); //ask
myViewHolder.buylow.setText(data1.dHigh);
if (newdatabid.size()< data.size())
{
newdatabid.add(data1.dBid); //Insert Value in array for the first time
}
if (olddatabid.size()< data.size())
{
olddatabid.add(data1.dBid); //Insert Value in array for the first time
}
if (newdataask.size()< data.size())
{
newdataask.add(data1.dAsk); //Insert Value in array for the first time
}
if (olddataask.size()< data.size()) //Insert Value in array for the first time
{
olddataask.add(data1.dAsk);
}
newdatabid.set(i,data1.dBid); //Store Value in array
newdataask.set(i,data1.dAsk); //Store Value in array
//Compare and perform Logic
if (Float.valueOf(newdatabid.get(i)) > Float.valueOf(olddatabid.get(i)))
{
myViewHolder.sellmax.setTextColor(Color.BLUE);
}
if (Float.valueOf(newdatabid.get(i)) < Float.valueOf(olddatabid.get(i)))
{
myViewHolder.sellmax.setTextColor(Color.RED);
}
if (Float.valueOf(newdataask.get(i)) > Float.valueOf(olddataask.get(i)))
{
myViewHolder.buymax.setTextColor(Color.BLUE);
}
if (Float.valueOf(newdataask.get(i)) < Float.valueOf(olddataask.get(i)))
{
myViewHolder.buymax.setTextColor(Color.RED);
}
olddatabid.set(i,newdatabid.get(i));
olddataask.set(i,newdataask.get(i));
}
});
}
}
#Override
public int getItemCount() {
return data.size();
}
}

Categories

Resources