I have created a RecyclerView to display items and everything shows and works fine except to one scenario: when I scroll fast to the bottom of the RecyclerView.
If I do so, I get the following error:
java.lang.ArrayIndexOutOfBoundsException: length=15; index=-1
It directs me to this part of my code (specifically to checkIfBorrowed.get( getAdapterPosition() )):
public class itemtestAdapter extends RecyclerView.Adapter<itemtestAdapter.testViewHolder> {
private List<Discoveritems> items;
private List<Boolean> checkIftested;
interface OnItemCheckListener {
void onItemCheck(Discoveritems items);
void onItemUncheck(Discoveritems items);
}
#NonNull
private OnItemCheckListener onItemCheckListener;
public itemtestAdapter(List<Discoveritems> items, List<Boolean> checkIftested, #NonNull OnItemCheckListener onItemCheckListener) {
this.items = items;
this.onItemCheckListener = onItemCheckListener;
this.checkIftested = checkIftested;
}
#Override
public testViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
view = LayoutInflater.from(parent.getContext()).inflate( R.layout.item_item_test_list, parent, false );
return new testViewHolder(view);
}
#Override
public void onBindViewHolder(testViewHolder holder, int position) {
holder.bind(items.get(position));
final Discoveritems currentItem = items.get(position);
holder.setOnClickListener( v -> {
holder.Cb_test.setChecked(
!holder.Cb_test.isChecked());
if (holder.Cb_test.isChecked()) {
onItemCheckListener.onItemCheck(currentItem);
} else {
onItemCheckListener.onItemUncheck(currentItem);
}
} );
}
#Override
public int getItemCount() {
return items.size();
}
class testViewHolder extends RecyclerView.ViewHolder {
ImageView Iv_test,itemPic;
TextView itemName;
CheckBox Cb_test;
Discoveritems items;
public testViewHolder(View itemView) {
super(itemView);
Iv_testitemPic = itemView.findViewById(R.id.iv_itemCover);
Tv_testitemName = itemView.findViewById( R.id.tv_testeditemName);
Cb_test = itemView.findViewById( R.id.cb_testitem );
Cb_test.setClickable( false );
}
public void setOnClickListener(View.OnClickListener onClickListener) {
itemView.setOnClickListener(onClickListener);
}
public void bind(Discoveritems items) {
this.items = items;
String itemID = items.getitemID();
MyitemClient client = new MyitemClient();
client.getitems(itemID, new JsonHttpResponseHandler(){
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response){
if(response!=null){
final Myitem items = Myitem.fromJson(response);
if (checkIftested.get( getAdapterPosition() )){
itemView.setEnabled(false);
Tv_itemName.setText("my" + items.getTitle());
} else {
Tv_itemName.setText( items.getTitle() );
}
}
}
});
}
}
}
Thing is that inside bind I can't figure out how to get the position of the item without using getAdapterPosition and I also cant understand why it only happens when I scroll fast.
Also it is weird that it says length = 15 while checkIfBorowed's size is 12 only.
Thank you
Change holder.bind(items.get(position)); to holder.bind(items.get(position), position); and then change viewholders bind method to accept the new position argument i.e. bind(Discoveritems items, int position) and use position instead of getAdapterPosition().
The issue is you are not using the position the adapter gave your viewholder which can change when views are recycled and thus they dont macth. Please see here for more https://stackoverflow.com/a/38970513/1133011
Related
I am working on an application where i have list of items in recyclerview on items i have a hear icon as wishlist the problem is when i click on the first item the last item automatically selected when i select the second item the second last got selected automatically. I dont know why this is happening?? please guide me
My RecyclerView Adapter
public class RecyclerviewAdapter extends RecyclerView.Adapter<RecyclerviewAdapter.Viewholder> {
ArrayList<RecyclerviewModel> datalist;
Context context;
public RecyclerviewAdapter(ArrayList<RecyclerviewModel> datalist, Context context) {
this.datalist = datalist;
this.context = context;
}
#NonNull
#Override
public Viewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View viewholder = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_item, parent , false);
return new Viewholder(viewholder);
}
#Override
public void onBindViewHolder(#NonNull Viewholder holder, int position)
{
holder.name.setText(datalist.get(position).getName());
holder.email.setText(datalist.get(position).getEmail());
holder.desc.setText(datalist.get(position).getDesc());
holder.book.setText(datalist.get(position).getBook());
//code for setting image-slider on home
holder.sliderAdapterExample = new SliderAdapterExample(context.getApplicationContext(), datalist.get(position).getImages());
holder.imageSlider.setSliderAdapter(holder.sliderAdapterExample);
final RecyclerviewModel datawish = datalist.get(position);
String wishName = datawish.getName();
String wishEmail = datawish.getEmail();
holder.wishlist.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
int pos = holder.getBindingAdapterPosition();
if(compoundButton.isChecked())
{
Toast.makeText(context, "item added to wishlist" + pos , Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, "item removed to wishlist", Toast.LENGTH_SHORT).show();
}
}
});
}
#Override
public int getItemCount() {
return datalist.size();
}
public class Viewholder extends RecyclerView.ViewHolder{
TextView name, email, desc, book;
SliderView imageSlider;
SliderAdapterExample sliderAdapterExample;
CheckBox wishlist;
public Viewholder(#NonNull View itemView) {
super(itemView);
name = itemView.findViewById(R.id.text_name);
email = itemView.findViewById(R.id.text_email);
desc = itemView.findViewById(R.id.text_desc);
book = itemView.findViewById(R.id.text_book);
wishlist = itemView.findViewById(R.id.wishlist_checkbox);
imageSlider = itemView.findViewById(R.id.imageView3);
}
}
}
i click on the first heart icon
the last one is automatically clicked
Why this happening? Because when you scroll recyclerview remove hidden items automatically. For ex: 0 will be remove when scroll down and 1 item will be 0, 2 item will be 1 and 3->2,4->3 like this. There are 2 ways fix this error:
1) Simple way, but this method freeze your app when data is big:
#Override
public void onBindViewHolder(#NonNull Viewholder holder, int position)
{
holder.setIsRecyclable(false);
}
2) If you want you use second method you must add unique_id to your arraylist each item:
public class RecyclerviewAdapter extends RecyclerView.Adapter<RecyclerviewAdapter.Viewholder> {
ArrayList<RecyclerviewModel> datalist;
Context context;
private ArrayList<String> checkedItems=new ArrayList<>();
public RecyclerviewAdapter(ArrayList<RecyclerviewModel> datalist, Context context) {
this.datalist = datalist;
this.context = context;
}
#NonNull
#Override
public Viewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View viewholder = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_item, parent , false);
return new Viewholder(viewholder);
}
#Override
public void onBindViewHolder(#NonNull Viewholder holder, int position)
{
int pos=holder.getAdapterPosition();
holder.name.setText(datalist.get(pos).getName());
holder.email.setText(datalist.get(pos).getEmail());
holder.desc.setText(datalist.get(pos).getDesc());
holder.book.setText(datalist.get(pos).getBook());
//code for setting image-slider on home
holder.sliderAdapterExample = new SliderAdapterExample(context.getApplicationContext(), datalist.get(pos).getImages());
holder.imageSlider.setSliderAdapter(holder.sliderAdapterExample);
final RecyclerviewModel datawish = datalist.get(pos);
String wishName = datawish.getName();
String wishEmail = datawish.getEmail();
holder.wishlist.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b){
checkedItems.add(datalist.get(pos).getUniqueId());
} else {
checkedItems.remove(datalist.get(pos).getUniqueId());
}
}
});
// Check your selection here
if(checkedItems.contains(datalist.get(pos).getUniqueId())){
holder.wishlist.setChecked(true);
} else {
holder.wishlist.setChecked(false);
}
}
#Override
public int getItemCount() {
return datalist.size();
}
public class Viewholder extends RecyclerView.ViewHolder{
TextView name, email, desc, book;
SliderView imageSlider;
SliderAdapterExample sliderAdapterExample;
CheckBox wishlist;
public Viewholder(#NonNull View itemView) {
super(itemView);
name = itemView.findViewById(R.id.text_name);
email = itemView.findViewById(R.id.text_email);
desc = itemView.findViewById(R.id.text_desc);
book = itemView.findViewById(R.id.text_book);
wishlist = itemView.findViewById(R.id.wishlist_checkbox);
imageSlider = itemView.findViewById(R.id.imageView3);
}
}
}
When you call your Adapter in your Fragment or Activity, add this line before setAdapter()
recyclerView.setItemViewCacheSize(mData.size());
I have a boolean in my onBindViewHolder which decides whether a button is clickable or not.
In the constructor I set this value to false, later when the user unlocks something I want to set it to true with a setter.
Here is my RecyclerViewAdapter.java:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private ArrayList<Items> items;
Context mContext;
private boolean unlocked;
public RecyclerViewAdapter(Context context, ArrayList<Items> items, boolean unlocked) {
mContext = context;
this.items = items;
this.unlocked = tabUnlocked;
}
public void setUnlocked(boolean unlocked) {
this.unlocked = unlocked;
this.notifyDataSetChanged();
}
#NonNull
#Override
public RecyclerViewAdapter.iewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.item, parent, false);
RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
v.setLayoutParams(lp);
return new RecyclerViewAdapter.ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull final RecyclerViewAdapter.ViewHolder holder, int position) {
final Items currentItem = items.get(position);
final String name = currentItem.getName();
holder.itemTextView.setText(name);
holder.itemTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (unlocked) {
//do something
}
}
});
}
#Override
public int getItemCount() {
return item.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView itemTextView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
itemTextView = itemView.findViewById(R.id.textViewItem);
}
}
}
And here is how I call the Setter to change it:
I make the RecyclerView public:
public RecyclerViewAdapter adapter;
Then I create an instance in my onCreate:
recyclerView = rootView.findViewById(R.id.recyclerView);
adapter = new RecyclerViewAdapter(items);
recyclerView.setAdapter(adapter);
And when the user unlocks a certain thing I call this method of the current Class:
public void unlockTab() {
adapter.setUnlocked(true);
}
When I log the boolean inside of that setter it tells me it got changed.
But when I log the boolean inside the onBindViewHolder it still remains false.
Why is the setter method not setting the boolean to true in the whole RecyclerViewAdapter.java class?
Hope someone can help me!
EDIT
I already tried adding "this.notifyDataSetChanged();" to the Setter Method (Thanks to #a_local_nobody)
Adapters are responsible for changes to recyclerviews. but if you don't tell them something changed, they won't know to do so.
call notifyDataSetChanged();
public void unlockTab() {
adapter.setUnlocked(true);
adapter.notifyDataSetChanged();
}
what does it do ?
It tells your recyclerview to bind all the data again, so then your changes will apply
So I have a fragment with three speedview gauges in it. Picture of fragment They used to be in the fragment directly and would work fine, but after I needed to add the textviews, I couldn't get everything to fit on the screen so I decided to create a custom layout and use a list view to display them.
It used to be very easy to update the speedometers and everythign worked fine.
After adding everything to a listview the only way I can think to get everything to update is to use notifyDataSetChanged() on the adapter, but when I do this, the gauge indicator snaps to 0 and goes up to the desired number from there. I have tried using a holder to maybe fix the problem, but no luck.
This is the fragment's code on how I create the adapter
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
OEEsegmentList = (ListView) view.findViewById(R.id.OEEplusLV);
qualitySegment = new OEEListSegment("Quality", OEEqualityValueString, "Bad parts: "+OEEqualityBadPartsValueString, "Total parts: "+OEEqualityTotalPartsValueString);
availabilitySegment = new OEEListSegment("Availability", OEEavailabilityValueString, "Downtime: "+OEEavailabilityDowntimeValueString, "Next time: "+OEEavailabilityNextTimeValueString);
performanceSegment = new OEEListSegment("Performance", OEEperformanceValueString, "Act time: "+OEEperformanceActTimeValueString, "Takt time: "+OEEperformanceTaktTimeValueString);
OEEsegmentArrayList = new ArrayList<>();
OEEsegmentArrayList.add(qualitySegment);
OEEsegmentArrayList.add(availabilitySegment);
OEEsegmentArrayList.add(performanceSegment);
OEEadapter = new OEEListAdapter(getActivity(), R.layout.oee_list_layout, OEEsegmentArrayList);
OEEsegmentList.setAdapter(OEEadapter);
}
and here is one of the update functions, they're all basically the same code with different names. I call them in the main activity when I recieve a message about the particular marker being changed.
public void updateOEEplusAvailability (String availability) {
OEEavailabilityValueString=availability;
availabilitySegment.setSegmentValueDial(OEEavailabilityValueString);
OEEadapter.notifyDataSetChanged();
}
this is my entire list adapter
public class OEEListAdapter extends ArrayAdapter<OEEListSegment> {
private Context mContext;
int mResource;
public ArrayList<OEEListSegment> mSegments ;
public OEEListAdapter(#NonNull Context context, int resource, #NonNull ArrayList<OEEListSegment> objects) {
super(context, resource, objects);
mContext = context;
mResource = resource;
mSegments=objects;
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
holder = new ViewHolder();
String name = getItem(position).getSegmentName();
String valueDial = getItem(position).getSegmentValueDial();
String valueTV1 = getItem(position).getSegmentValueTV1();
String valueTV2 = getItem(position).getSegmentValueTV2();
final OEEListSegment Segment = new OEEListSegment(name, valueDial, valueTV1, valueTV2);
LayoutInflater inflater = LayoutInflater.from(mContext);
convertView = inflater.inflate(mResource, parent, false);
TextView textViewName = (TextView) convertView.findViewById(R.id.textViewSegmentName);
holder.speedView = (SpeedView) convertView.findViewById(R.id.segmentSpeedView);
holder.textViewFirst = (TextView) convertView.findViewById(R.id.textViewSegmentFirst);
holder.textViewSecond=(TextView) convertView.findViewById(R.id.textViewSegmentSecond);
convertView.setTag(holder);
holder.textViewFirst.setText(mSegments.get(position).getSegmentValueTV1());
holder.textViewSecond.setText(mSegments.get(position).getSegmentValueTV2());
holder.speedView.speedTo(Float.parseFloat(mSegments.get(position).getSegmentValueDial()));
holder.speedView.clearSections();
holder.speedView.addSections(
new Section(.3f, Color.RED)
, new Section(.7f, Color.YELLOW)
, new Section(1f, Color.GREEN));
textViewName.setText(name);
holder.textViewFirst.setText(valueTV1);
holder.textViewSecond.setText(valueTV2);
holder.speedView.speedTo(Float.parseFloat(valueDial));
return convertView;
}
#Override
public int getViewTypeCount() {
return getCount();
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public int getCount() {
return mSegments.size();
}
public Object getItemCustom(int position) {
return mSegments.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
private class ViewHolder {
protected SpeedView speedView ;
private TextView textViewFirst ;
private TextView textViewSecond;
}
}
And this is the list segment code
public class OEEListSegment implements Parcelable {
private String name;
private String valueDial;
private String valueTV1;
private String valueTV2;
public OEEListSegment(String name, String valueDial, String valueTV1, String valueTV2) {
this.name=name;
this.valueDial=valueDial;
this.valueTV1=valueTV1;
this.valueTV2=valueTV2;
}
public String getSegmentName() {
return name;
}
public void setSegmentName(String name) {
this.name=name;
}
public String getSegmentValueDial() {
return valueDial;
}
public void setSegmentValueDial(String valueDial) {
this.valueDial=valueDial;
}
public String getSegmentValueTV1() {
return valueTV1;
}
public void setSegmentValueTV1(String valueTV1) {
this.valueTV1=valueTV1;
}
public String getSegmentValueTV2() {
return valueTV2;
}
public void setSegmentValueTV2(String valueTV2) {
this.valueTV2=valueTV2;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(new String[] {this.name, this.valueDial, this.valueTV1, this.valueTV2});
}
private void readFromParcel(Parcel in) {
name = in.readString();
valueDial= in.readString();
valueTV1= in.readString();
valueTV2= in.readString();
}
public OEEListSegment(Parcel in){
String[] data = new String[4];
in.readStringArray(data);
// the order needs to be the same as in writeToParcel() method
this.name = data[0];
this.valueDial= data[1];
this.valueTV1= data[2];
this.valueTV2= data[3];
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public OEEListSegment createFromParcel(Parcel in) {
return new OEEListSegment(in);
}
public OEEListSegment[] newArray(int size) {
return new OEEListSegment[size];
}
};
}
Any solution would be appreciated. Either how to stop that initial animation (0 to desired number) from happening in the speed view or how to update the listview so that animation doesn't happen.
Or would it be best to omit the list view completely, while still getting this desired layout (there will only ever be 3 segments).
I wasn't able to do what I needed to do inside the list segment, but since I knew the exact number of times I wanted this segment to be shown, I decided to change my approach.
I added a scoll view in my main layout and then included the segment layout 3x inside it. I then needed to access all the components by ID like this to acces the segment (layout)
performanceLayout = view.findViewById(R.id.performanceSegment);
and like this to access the elements inside my segments
speedViewPerformance= performanceLayout.findViewById(R.id.segmentSpeedView);
then I could update the speeviews with funcktions like this
public void updateOEEplusPerformance (String performance) {
OEEperformanceValueString=performance;
speedViewPerformance.speedTo(Float.parseFloat(OEEperformanceValueString));
}
It's much simpler than what I was trying to do before, since I don't need an adapter or a new class, I definitely do not need to change the speedview animation and it works just like I needed it to!
I have made a note app and everything works fine. Just in RecyclerView List when I choose an item or items and in menu I click delete button I the note is not deleted. Although delete method works in another place.
Here's the method of delete button in menu :
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.delete_selected) {
for (NotePad notePad : notes) {
if (selectedIds.contains(notePad.getId())) {
database = new Database(this);
database.deleteNote(notePad.getId());
}
}
adapter.notifyDataSetChanged();
return true;
}
return false;
}
And method of delete note in my database class:
void deleteNote(long id) {
SQLiteDatabase database = this.getWritableDatabase();
database.delete(DATABASE_TABLE, ID + "=?", new String[] {String.valueOf(id)});
database.close();
}
I don't know how to relate id of selected note to my method cause deletNote method receives long value but my selectedId is List.
Thanks in advance.
UPDATED
I realized my code works find but it doesn't refresh the list and also goes back from ActionMode to Toolbar. I tried adapter.notifyDataSetChanged();
Use this adapter. I have used interface.
public class NotePadAdapter extends RecyclerView.Adapter < NotePadAdapter.ViewHolder > {
private Context context;
private LayoutInflater inflater;
private List < NotePad > notes;
private List < Long > selectedIds = new ArrayList < > ();
private NoteClickListener listener;
NotePadAdapter(Context context, NoteClickListener listener, List < NotePad > notes) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.notes = notes;
this.listener = listener;
}
public interface NoteClickListener {
public void onNoteClick(NotePad notePad);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.notes_list, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
String title = notes.get(position).getTitle();
String date = notes.get(position).getDate();
String time = notes.get(position).getTime();
holder.setOnClickListener() {
listener.onNoteClick(notes.get(position))
}
holder.noteTitle.setText(title);
holder.noteTitle.setText(title);
holder.noteDate.setText(date);
holder.noteTime.setText(time);
long id = notes.get(position).getId();
if (selectedIds.contains(id)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
holder.rooView.setForeground(new ColorDrawable(ContextCompat.getColor(context, R.color.colorControlActivated)));
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
holder.rooView.setForeground(new ColorDrawable(ContextCompat.getColor(context, android.R.color.transparent)));
}
}
}
#Override
public int getItemCount() {
return notes.size();
}
public NotePad getItem(int position) {
return notes.get(position);
}
public void setSelectedIds(List < Long > selectedIds) {
this.selectedIds = selectedIds;
notifyDataSetChanged();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView noteTitle, noteDate, noteTime;
ConstraintLayout rooView;
ViewHolder(#NonNull View itemView) {
super(itemView);
noteTitle = itemView.findViewById(R.id.note_title);
noteDate = itemView.findViewById(R.id.note_date);
noteTime = itemView.findViewById(R.id.note_time);
rooView = itemView.findViewById(R.id.root_view);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(v.getContext(), ContentActivity.class);
i.putExtra("ID", notes.get(getAdapterPosition()).getId());
v.getContext().startActivity(i);
}
});
}
}
}
In your activity/fragment where you are initiating this adapter pass this for the listener and implement the interface and whenever you click any item on the recyclerview then you will your whole NotePad object inside this function.
Having a problem when scrolling RecyclerView after scrolling down and up. The idea is to change elements color, but when I scroll down everything is great and when the scroll goes up - the elements, which are shouldn't be colored are changing color.
Here's my adapter:
public class NotificationsAdapter extends RecyclerView.Adapter<NotificationsAdapter.ViewHolder> {
private NotificationData notificationData;
private Context mContext;
private ArrayList<NotificationData> infromationList = new ArrayList<>();
public NotificationsAdapter(Context context, ArrayList<NotificationData> infromationList) {
this.infromationList = infromationList;
this.mContext = context;
}
#Override
public NotificationsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemLayoutView;
ViewHolder viewHolder;
itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.notification_single_item, parent, false);
viewHolder = new ViewHolder(itemLayoutView, viewType);
return viewHolder;
}
#Override
public void onBindViewHolder(NotificationsAdapter.ViewHolder holder, int position) {
notificationData = infromationList.get(position);
holder.notificationDate.setText(convertDate(notificationData.getDate()));
holder.notificationStatus.setText(notificationData.getNotificationStatus());
holder.orderDescription.setText(notificationData.getNotificationLabel());
if ("true".equals(notificationData.getReadStatus())) {
holder.root.setBackgroundColor(mContext.getResources().getColor(R.color.white));
holder.notificationStatus.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
}
}
#Override
public int getItemCount() {
return (null != infromationList ? infromationList.size() : 0);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView notificationDate;
public TextView notificationStatus;
public TextView orderDescription;
public LinearLayout root;
public ViewHolder(View itemView, int position) {
super(itemView);
notificationDate = (TextView) itemView.findViewById(R.id.notificationDate);
notificationStatus = (TextView) itemView.findViewById(R.id.notificationStatus);
orderDescription = (TextView) itemView.findViewById(R.id.orderDescription);
root = (LinearLayout) itemView.findViewById(R.id.root);
}
}
private String convertDate(String date) {
String convertedDate;
String[] parts = new String[2];
parts = date.split("T");
date = parts[0];
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
Date testDate = null;
try {
testDate = sdf.parse(date);
}catch(Exception ex){
ex.printStackTrace();
}
SimpleDateFormat formatter = new SimpleDateFormat("dd.mm.yyyy");
convertedDate = formatter.format(testDate);
return convertedDate;
}
}
I had the same problem and the only solution I found for this is:
holder.setIsRecyclable(false);
Your recycler will not recycle anymore so the items will be the same when you scroll, and if you want to delete some item do not use notifyitemRemoved(position), use notifyDataSetChanged() instead.
Add setHasStableIds(true); in your adapter constructor and
Override these two methodes in adapter.
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
There is problem in your onBindViewHolder(...), should be:
if ("true".equals(notificationData.getReadStatus())) {
holder.root.setBackgroundColor(mContext.getResources().getColor(R.color.white));
holder.notificationStatus.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
}
else {
holder.root.setBackgroundColor(yourDefaultColor);
holder.notificationStatus.setTypeface(yourDefaultTypeface);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
final UserData userdata = userdataList.get(position);
holder.setIsRecyclable(false);
holder.name.setText(userdata.getName());
holder.active.setChecked(userdata.getActive());
String userPic = userdata.getPic();
holder.active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked){
userdata.setActive(isChecked);
}
});
}
onBindHolder called several times as Recycler View needs a view unless new one. So each time you set visilibity in child views, other views states are also changes.
Whenever you scroll up and down, these views are getting re-drawed with wrong visibility options so always specify both the conditions cause recycler view doesn't know the previous state/conditions/values of our widgets.
Solution :
If in If block you set visibility of any android widget.setVisibility(View.Gone) then in else block you have to set it's visibility opposite value like widget.setVisibility(View.Visible) to overcome the above problem.
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
viewHolder.tvName.setText(ModelCategoryProducts.name.get(i));
viewHolder.tvPrice.setText("Rs."+String.format("%.2f", Float.parseFloat(ModelCategoryProducts.price.get(i))));
if(ModelCategoryProducts.special_price.get(i).equals("null")) {
viewHolder.tvSpecialPrice.setVisibility(View.GONE); // here visibility is gone and in else it's opposite visibility i set.
viewHolder.tvPrice.setTextColor(Color.parseColor("#ff0000"));
viewHolder.tvPrice.setPaintFlags(0);// here paint flag is 0 and in else it's opposite flag that i want is set.
}else if(!ModelCategoryProducts.special_price.get(i).equals("null")){
viewHolder.tvPrice.setTextColor(Color.parseColor("#E0E0E0"));
viewHolder.tvSpecialPrice.setVisibility(View.VISIBLE);
viewHolder.tvSpecialPrice.setText("Rs." + String.format("%.2f", Float.parseFloat(ModelCategoryProducts.special_price.get(i))));
viewHolder.tvPrice.setPaintFlags(viewHolder.tvPrice.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
if (!ModelCategoryProducts.image_url.get(i).isEmpty()) {
Picasso.with(context)
.load(ModelCategoryProducts.image_url.get(i))
.into(viewHolder.ivProduct);
}
viewHolder.setClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean isLongClick) {
if (isLongClick) {
// Toast.makeText(context, "#" + position + " - " + ModelCategoryProducts.name.get(position) + " (Long click)", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "#" + position + " - " + ModelCategoryProducts.name.get(position), Toast.LENGTH_SHORT).show();
Intent i = new Intent(context, ProductDetail.class);
i.putExtra("position",position);
i.putExtra("flagHlvCheck", 5);
context.startActivity(i);
}
}
});
}
Try adding this in the adapter.
#Override
public int getItemViewType(int position)
{
return position;
}
If someone might face issues with some of the fields in the viewholder getting random values, then try to set all the fields with atleast any default value.
#Override
public DataObjectHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.custom_layout, parent, false);
DataObjectHolder dataObjectHolder = new DataObjectHolder(view);
dataObjectHolder.setIsRecyclable(false);
return dataObjectHolder;
}
The best way is indicate an ArrayList for example as a Model and have some parameters and define setter and getter for that.
package com.test.mohammaddvi.snappfood.Model;
public class OfferList {
private boolean visibilityOrder;
private int number;
public OfferList(int number, boolean visibilityOrder) {
this.number=number;
this.visibilityOrder=visibilityOrder;
}
public boolean isVisibilityOrder() {
return visibilityOrder;
}
public void setVisibilityOrder(boolean visibilityOrder) {
this.visibilityOrder = visibilityOrder;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
and set the the variables as where you want and for get you must do it in onBindViewHolder of your recyclerview Adapter:
if (offerList.isVisibilityOrder()) {
holder.foodMinusButton.setVisibility(View.VISIBLE);
holder.foodOrderNumber.setText(offerList.getNumber() + "");
holder.foodOrderNumber.setVisibility(View.VISIBLE);
} else {
holder.foodMinusButton.setVisibility(View.INVISIBLE);
}
and indicate it your recyclerview adapter:
public class RecyclerViewMenuFragmentAdapter extends RecyclerView.Adapter<RecyclerViewMenuFragmentAdapter.SingleItemInMenuFragment> {
private ArrayList<Food> foodList;
private Context mContext;
private List<OfferList> offers;
public RecyclerViewMenuFragmentAdapter(ArrayList<Food> foodList, Context mContext, List<OfferList> offers) {
this.foodList = foodList;
this.mContext = mContext;
this.offers = offers;
}
class AnyRVAdapter: androidx.recyclerview.widget.RecyclerView.Adapter<AnyRVAdapter.MViewHolder>() {
// put saver outside viewholder
val saveLayId = mutableListOf<Int>()
inner class MViewHolder(itemView: View) :
androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
fun bindModel(d: TesListModel.MList, position:Int) {
// concept here
val showedId= saveLayId.find { s -> s == layoutPosition}
if (idClicked == null) {
// save the layout id
lyClicked.visibility = View.VISIBLE
saveLayId.add(layoutPosition)
} else {
// remove the layout id
lyClicked.visibility = View.INVISIBLE
saveLayId.remove(layoutPosition)
}
}
}
but i think this code is heavy if you use for large data set.
Guys this has worked for me..
override fun setHasStableIds(hasStableIds: Boolean) {
setHasStableIds(true)
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}