Having problem when clicking items of RecyclerView, app crashes and giving the output:
java.lang.IndexOutOfBoundsException: Invalid index 24, size is 20
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
at java.util.ArrayList.get(ArrayList.java:308)
at com.google.gson.JsonArray.get(JsonArray.java:147)
at com.devpocket.kvartirka.MainActivity.itemClicked(MainActivity.java:334)
at com.devpocket.kvartirka.Adapters.OffersAdapter$ViewHolder.onClick(OffersAdapter.java:161)
at android.view.View.performClick(View.java:5162)
at android.view.View$PerformClick.run(View.java:20873)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5834)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1388)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183)
java.lang.IndexOutOfBoundsException: Invalid index 24, size is 20
The code which I'm using is like this, this is an adapter:
public class OffersAdapter extends RecyclerView.Adapter<OffersAdapter.ViewHolder> {
private ClickListener clickListener;
private CityData cityData;
private ArrayList<CityData> cityItemList = new ArrayList<CityData>();
private Context mContext;
private String text;
public static Button headerButton1;
public OffersAdapter(Context context, ArrayList<CityData> cityItemList, String text) {
this.cityItemList = cityItemList;
this.mContext = context;
this.text = text;
}
#Override
public int getItemViewType(int position) {
int viewType;
if (position == 0) {
viewType = 0;
} else {
viewType = 1;
}
return viewType;
}
#Override
public OffersAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, final int viewType) {
View itemLayoutView;
ViewHolder viewHolder;
if (viewType == 0) {
itemLayoutView = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.logo_layout, viewGroup, false);
viewHolder = new ViewHolder(itemLayoutView, viewType);
} else {
itemLayoutView = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.offers_singleitem, viewGroup, false);
viewHolder = new ViewHolder(itemLayoutView, viewType);
}
return viewHolder;
}
#Override
public void onBindViewHolder(OffersAdapter.ViewHolder viewHolder, int position) {
if (position > 0) {
cityData = cityItemList.get(position-1);
String type = cityData.getType();
if ("flat".equals(type)) {
viewHolder.address.setText(cityData.getAddress());
viewHolder.description.setText(cityData.getDescription());
viewHolder.roomNumbers.setText(cityData.getRoomNumbers());
if (TextUtils.isEmpty(cityData.getMetro())) {
viewHolder.metroImageView.setVisibility(View.GONE);
}
viewHolder.metro.setText(cityData.getMetro());
viewHolder.prices.setText(cityData.getPrices() + " ₽");
Ion.with(viewHolder.offerImage)
.fitXY()
.load(cityData.getURL());
} else if("cottage".equals(type)) {
viewHolder.address.setText(cityData.getAddress());
viewHolder.description.setText(cityData.getDescription());
viewHolder.roomNumbers.setVisibility(View.GONE);
viewHolder.cottageImage.setVisibility(View.VISIBLE);
if (TextUtils.isEmpty(cityData.getMetro())) {
viewHolder.metroImageView.setVisibility(View.GONE);
}
viewHolder.metro.setText(cityData.getMetro());
viewHolder.prices.setText(cityData.getPrices() + " ₽");
Ion.with(viewHolder.offerImage)
.fitXY()
.load(cityData.getURL());
}
}
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView roomNumbers; //количество комнат
public TextView address; //адресс квартиры
public TextView description; //описание квартиры
public TextView metro; //описание квартиры
public TextView prices; //цена за день
public ImageView offerImage; //картинка квартиры
public ImageView cottageImage; //обозначение коттеджа
public ImageView metroImageView; //значок метро
public ViewHolder(View itemView, int position) {
super(itemView);
if(position > 0) {
roomNumbers = (TextView) itemView.findViewById(R.id.roomNumbers);
address = (TextView) itemView.findViewById(R.id.addressTextView);
description = (TextView) itemView.findViewById(R.id.conditionsTextView);
metro = (TextView) itemView.findViewById(R.id.metroTextView);
offerImage = (ImageView) itemView.findViewById(R.id.imageView);
prices = (TextView) itemView.findViewById(R.id.priceTV);
cottageImage = (ImageView) itemView.findViewById(R.id.cottageImage);
metroImageView = (ImageView) itemView.findViewById(R.id.metroImageView);
itemView.setOnClickListener(this);
} else {
headerButton1 = (Button) itemView.findViewById(R.id.headerButton1);
headerButton1.setText(Html.fromHtml(text));
headerButton1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, FilterActivity.class);
mContext.startActivity(intent);
}
});
}
}
#Override
public void onClick(View v) {
if(clickListener != null) {
notifyDataSetChanged();
clickListener.itemClicked(v, getPosition());
}
}
}
#Override
public int getItemCount() {
return (null != cityItemList ? cityItemList.size() : 0);
}
public void setClickListener(ClickListener clickListener) {
this.clickListener = clickListener;
}
public interface ClickListener {
public void itemClicked(View view, int position);
}
}
And this is how adding data into adapter:
private ArrayList<CityData> cityList = new ArrayList<CityData>();
CityData cityItem = new CityData();
cityItem.setAddress(String.valueOf(flatAddress));
cityItem.setType(type);
cityItem.setDescription(description);
cityItem.setRoomNumbers(rooms);
cityItem.setMetro(metro);
cityItem.setUrl(imageURL);
cityItem.setPrices(day);
cityList.add(cityItem);
adapter.notifyDataSetChanged();
adapter.setClickListener(MainActivity.this);
And the on itemClicked
#Override
public void itemClicked(View view, int position) {
String offerData = null;
for(int i = 0; i <= position; i++ ) {
if(i == position) {
JsonElement obj = jsonObjectToPass.get(position-1);
offerData = String.valueOf(obj);
}
}
Intent intent = new Intent(MainActivity.this, OfferInfo.class);
intent.putExtra("offerData", offerData);
intent.putExtra("position", position);
startActivity(intent);
}
Change itemClicked() to this
#Override
public void itemClicked(View view, int position) {
String offerData = null;
JsonElement obj = jsonObjectToPass.get(position-1);
offerData = String.valueOf(obj);
Intent intent = new Intent(MainActivity.this, OfferInfo.class);
intent.putExtra("offerData", offerData);
intent.putExtra("position", position);
startActivity(intent);
}
No need of for loop while you are getting the position directly
Your problem is this line: JsonElement obj = jsonObjectToPass.get(position-1);
Without further information, you try to retrieve the element #24 (position == 25) when there is only 20 items in the list.
Maybe add a validation in your if like this:
if(i == position && jsonObjectToPass.size() < position) {
And check your condition, it seems wrong. Your code will enter in the if only one time in the for loop. So why putting a loop there ?
Related
I have a recycler view to show an activity timeline and three floating buttons that click to add activity. How can I code three buttons to a different view? How can I pass the condition to the Adapter?
Now I can show only one viewType.
thx a lot.
Adapter.java
public class TripAdapter extends RecyclerView.Adapter<TripAdapter.TripHolder> {
private List<Trip> trips = new ArrayList<>();
private OnItemClickListener listener;
#NonNull
#Override
public TripHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.trip_item, parent, false);
return new TripHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull TripHolder holder, int position) {
Trip currentTrip = trips.get(position);
holder.textViewLodgingTitle.setText(currentTrip.getLodgingTitle());
holder.textTextViewLodgingCheckInDateTime.setText(currentTrip.getLodgingCheckInDateTime());
holder.textTextViewLodgingCheckOutDateTime.setText(currentTrip.getLodgingCheckOutDateTime());
holder.textViewLodgingAddress.setText(currentTrip.getLodgingAddress());
}
#Override
public int getItemCount() {
return trips.size();
}
public void setTrips(List<Trip> trips) {
this.trips = trips;
notifyDataSetChanged();
}
public Trip getTripAt(int position) {
return trips.get(position);
}
class TripHolder extends RecyclerView.ViewHolder {
//lodging
private TextView textViewLodgingTitle;
private TextView textTextViewLodgingCheckInDateTime;
private TextView textTextViewLodgingCheckOutDateTime;
private TextView textViewLodgingAddress;
private TextView textViewLodgingPhone;
private TextView textViewLodgingWebsite;
private TextView textViewLodgingEmail;
public TripHolder(#NonNull View itemView) {
super(itemView);
context = itemView.getContext();
textViewLodgingTitle = itemView.findViewById(R.id.text_view_title);
textTextViewLodgingCheckInDateTime = itemView.findViewById(R.id.text_view_start_date_time);
textTextViewLodgingCheckOutDateTime = itemView.findViewById(R.id.text_view_end_date_time);
textViewLodgingAddress = itemView.findViewById(R.id.text_view_description);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int position = getAdapterPosition();
if (listener != null && position != RecyclerView.NO_POSITION) {
listener.onItemClick(trips.get(position));
}
}
});
}
}
public interface OnItemClickListener {
void onItemClick(Trip trip);
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
}
MainActivity.java
//adapter
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
final TripAdapter adapter = new TripAdapter();
recyclerView.setAdapter(adapter );
tripViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication())).get(TripViewModel.class);
tripViewModel.getAllTrips().observe(this, new Observer<List<Trip>>() {
#Override
public void onChanged(#Nullable List<Trip> trips) {
adapter.setTrips(trips);
}
});
//this on onActivityResult()
else if (requestCode == ADD_LODGING && resultCode == Activity.RESULT_OK) {
//get data from lodging activity
String lodgingTitle = data.getStringExtra(LodgingEditActivity.EXTRA_LODGING_TITLE);
String lodgingCheckInDateTime = data.getStringExtra(LodgingEditActivity.EXTRA_LODGING_CHECK_IN_DATE_TIME);
String lodgingCheckOutDateTime = data.getStringExtra(LodgingEditActivity.EXTRA_LODGING_CHECK_OUT_DATE_TIME);
String lodgingDescription = data.getStringExtra(LodgingEditActivity.EXTRA_LODGING_DESCRIPTION);
String lodgingAddress = data.getStringExtra(LodgingEditActivity.EXTRA_LODGING_ADDRESS);
String lodgingPhone = data.getStringExtra(LodgingEditActivity.EXTRA_LODGING_PHONE);
String lodgingWebsite = data.getStringExtra(LodgingEditActivity.EXTRA_LODGING_WEBSITE);
String lodgingEmail = data.getStringExtra(LodgingEditActivity.EXTRA_LODGING_EMAIL);
String lodgingImagePath = "test";
Trip lodging = new Trip(lodgingTitle, lodgingCheckInDateTime, lodgingCheckOutDateTime,lodgingDescription, lodgingAddress, lodgingPhone, lodgingWebsite, lodgingEmail,lodgingImagePath);
tripViewModel.insert(lodging);
Toast.makeText(this, "lodging save", Toast.LENGTH_SHORT).show();
}
My application I adapt from codinginflow channel.
https://codinginflow.com/tutorials/android/room-viewmodel-livedata-recyclerview-mvvm/part-1-introduction
I got a weird exception in the function onCreateViewHolder inside my custom adapter. Probably because getItemViewType function returns different values as expected.
This is how getItemViewType function looks like:
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
#Override
public int getItemViewType(int position) {
if (position == 0)
return TYPE_HEADER;
return TYPE_ITEM;
}
As you can see, it returns 0 or 1 only, no other option.
This is how onCreateViewHolder function looks like:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_groups, null);
return new HeaderHolder(view);
} else if(viewType == TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.explore_group_row, null);
return new ItemHolder(view);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " make sure your using types correctly");
}
However, I got some crashes from crashlytics that there is no type that matches with viewTypes of 6, 7, 8, 11, etc.. Not sure where it gets from...
In addition, I can't reproduce this issue, no idea how it happens.
I saw a similar issue in this thread but no solution.
Here you can see the exception from crashlytics:
Fatal Exception: java.lang.RuntimeException: there is no type that matches the type 6 make sure your using types correctly
at com.example.eliran.forum.GroupsFragment$GroupsAdapter.onCreateViewHolder(GroupsFragment.java:72)
at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:5)
at androidx.recyclerview.widget.RecyclerView$Recycler.a(RecyclerView.java:295)
at androidx.recyclerview.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:14)
at androidx.recyclerview.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:15)
at androidx.recyclerview.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:22)
at androidx.recyclerview.widget.GapWorker.a(GapWorker.java:3)
at androidx.recyclerview.widget.GapWorker.run(GapWorker.java:71)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7078)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
Here's the full code of the adapter:
private class GroupsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_groups, null);
return new HeaderHolder(view);
} else if(viewType == TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.explore_group_row, null);
return new ItemHolder(view);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " make sure your using types correctly");
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
if (holder instanceof ItemHolder) {
ItemHolder theHolder = (ItemHolder) holder;
Group group = groups.get(i - 1);
theHolder.title.setText(group.getTitle());
theHolder.description.setText(group.getDescription());
if(group.getImage() != null) {
Picasso.get()
.load(group.getImage())
.fit()
.into(theHolder.image);
} else {
Picasso.get()
.load(R.drawable.profile_picture_holder)
.fit()
.into(theHolder.image);
}
} else if (holder instanceof HeaderHolder){
HeaderHolder theHolder = (HeaderHolder) holder;
if (showMyGroupsPb) {
theHolder.myGroupsPb.setVisibility(View.VISIBLE);
theHolder.myGroupSectionHeader.setOnBtnListener(null);
} else {
theHolder.myGroupsPb.setVisibility(View.GONE);
theHolder.myGroupSectionHeader.setOnBtnListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(rootView.getContext(), MyGroupsActivity.class);
intent.putExtra("groups", myGroups);
startActivityForResult(intent, TheFinals.JOIN_LEAVE_GROUP_CODE);
}
});
}
}
}
#Override
public int getItemViewType(int position) {
if (position == 0)
return TYPE_HEADER;
return TYPE_ITEM;
}
#Override
public int getItemCount() {
return groups.size() + 1;
}
class HeaderHolder extends RecyclerView.ViewHolder {
private ProgressBar myGroupsPb;
private SectionHeader myGroupSectionHeader;
public HeaderHolder(View view) {
super(view);
myGroupsRv = view.findViewById(R.id.groupsRecyclerView);
myGroupsPb = view.findViewById(R.id.pb);
LinearLayoutManager horizontalLayoutManager
= new LinearLayoutManager(rootView.getContext(), LinearLayoutManager.HORIZONTAL, false);
myGroupsRv.setLayoutManager(horizontalLayoutManager);
myGroupsRv.setAdapter(myGroupAdapter);
myGroupSectionHeader = view.findViewById(R.id.myGroupSectionHeader);
}
}
class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
protected TextView title;
protected ImageView image;
protected TextView description;
public ItemHolder(View view) {
super(view);
this.title = view.findViewById(R.id.title);
this.image = view.findViewById(R.id.image);
this.description = view.findViewById(R.id.description);
view.setOnClickListener(this);
}
#Override
public void onClick(View view) {
int itemPosition = groupsRecyclerView.getChildLayoutPosition(view) - 1;
selectedGroupIndex = itemPosition;
Intent intent = new Intent(rootView.getContext(), GroupActivity.class);
intent.putExtra("group", groups.get(itemPosition));
startActivityForResult(intent, TheFinals.JOIN_LEAVE_GROUP_CODE);
}
}
}
When scroll recyclerview some items mixes. After I add ads after every 15 items, holder get wrong data. Some items are vip items. I will change background color of these items. But when I scroll it dublicates mixes. How can I solve?
This is my adapter
private Context mCtx;
private List<Car> carList;
private RecyclerViewAnimator mAnimator;
private int AD_TYPE=1;
private int CONTENT_TYPE=2;
private int LIST_AD_DELTA=15;
public ProductAllCarAdapter(RecyclerView recyclerView,Context mCtx, List<Car> carList) {
this.mCtx = mCtx;
this.carList = carList;
mAnimator = new RecyclerViewAnimator(recyclerView);
}
#Override
public ProductAllCarAdapter.ProductViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == AD_TYPE){
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_add_item, null);
ProductAllCarAdapter.ProductViewHolder vh = new ProductAllCarAdapter.ProductViewHolder(itemView);
mAnimator.onCreateViewHolder(itemView);
return vh;
} else {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_car_item, null);
ProductAllCarAdapter.ProductViewHolder vh = new ProductAllCarAdapter.ProductViewHolder(itemView);
mAnimator.onCreateViewHolder(itemView);
return vh;
}
}
#Override
public int getItemViewType(int position) {
if (position>0 && position % LIST_AD_DELTA == 0)
return AD_TYPE;
return CONTENT_TYPE;
}
#Override
public void onBindViewHolder(ProductAllCarAdapter.ProductViewHolder holder, int position) {
if (getItemViewType(position) == CONTENT_TYPE) {
final Car car = carList.get(holder.getAdapterPosition());
GlideApp.with(mCtx).load(car.getImg()).into(holder.imageView);
if (car.getVip() == 1) {
holder.relativeLayout.setBackgroundColor(ContextCompat.getColor(mCtx, R.color.colorVip));
holder.imageViewVIP.setVisibility(View.VISIBLE);
}
final String carid = String.valueOf(car.getCarid());
mAnimator.onBindViewHolder(holder.itemView, position);
} else {
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Context mcontext = view.getContext();
Bundle bundle = ActivityOptionsCompat.makeCustomAnimation(mcontext, android.R.anim.fade_in, android.R.anim.fade_out).toBundle();
Intent intent = new Intent(mcontext, AdsItem.class);
mcontext.startActivity(intent, bundle);
}
});
mAnimator.onBindViewHolder(holder.itemView, position);
}
}
private int getRealPosition(int position) {
if (LIST_AD_DELTA == 0) {
return position;
} else {
return position - position / LIST_AD_DELTA;
}
}
#Override
public long getItemId(int position) { return position; }
#Override
public int getItemCount() {
int additionalContent = 0;
if (carList.size() > 0 && carList.size() > LIST_AD_DELTA) {
additionalContent = ( carList.size() / LIST_AD_DELTA);
}
return carList.size() + additionalContent;
}
public static class ProductViewHolder extends RecyclerView.ViewHolder {
private View mView;
ImageView imageView, imageViewVIP;
RelativeLayout relativeLayout;
public ProductViewHolder(View itemView) {
super(itemView);
mView = itemView;
imageView = itemView.findViewById(R.id.imageView);
imageViewVIP = itemView.findViewById(R.id.imageViewVIP);
relativeLayout = itemView.findViewById(R.id.relativeLayoutpc);
}
public void setOnClickListener(View.OnClickListener listener) {
mView.setOnClickListener(listener);
}
}
I think problem onBindViewHolder function use wrong holder. ArrayList also return true value but on scroll it mixes.
You just have to add the corresponding else of the following if statement block.
if (car.getVip() == 1) {
holder.relativeLayout.setBackgroundColor(ContextCompat.getColor(mCtx, R.color.colorVip));
holder.imageViewVIP.setVisibility(View.VISIBLE);
} else {
holder.relativeLayout.setBackgroundColor(ContextCompat.getColor(mCtx, R.color.colorNormal));
holder.imageViewVIP.setVisibility(View.GONE);
}
This is inside your onBindViewHolder function where the view type is CONTENT_TYPE.
Hope that solves your problem.
I want to implement a simple custom multiselection RecyclerView.
My problem is when I select the first CardView element in the screen, the tenth Card is also selected (When I select the second, the eleventh card is selected etc.). I have tried many different such as using `notifyItemChanged(pos) but I always end up to the same bug.
Here is the code if the my custom class.
public class StudentRecyclerAdapter extends RecyclerView.Adapter<StudentRecyclerAdapter.CustomViewHolder> {
private List<Student> studentListItemList;
private Context mContext;
private Students students;
private Set<Integer> studensToBeRemoved;
private List<Boolean> selected;
public StudentRecyclerAdapter(Context context, List<Student> studentListItemList, Students students) {
this.studentListItemList = studentListItemList;
this.mContext = context;
this.studensToBeRemoved = new HashSet<>();
this.students = students;
selected= new ArrayList<>();
for(int i=0;i<studentListItemList.size();i++)
selected.add(false);
}
View.OnClickListener clickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
CustomViewHolder holder = (CustomViewHolder) view.getTag();
int position = holder.getAdapterPosition();
Student studentListItem = studentListItemList.get(position);
Bundle extras = new Bundle();
extras.putInt("id", studentListItem.getId());
extras.putString("name", studentListItem.getName());
extras.putString("surname", studentListItem.getSurname());
extras.putLong("date", studentListItem.getLongBirthday());
extras.putString("photo", studentListItem.getImage());
Intent intent = new Intent(mContext, ProfileStudent.class);
intent.putExtras(extras);
mContext.startActivity(intent);
}
};
View.OnLongClickListener longClickListener = view -> {
final CustomViewHolder holder = (CustomViewHolder) view.getTag();
int pos = holder.getAdapterPosition();
if (!selected.get(pos)) {
holder.stud_card_view.setCardBackgroundColor(Color.GREEN);
studensToBeRemoved.add(studentListItemList.get(pos).getId());
selected.set(pos,true);
} else {
holder.stud_card_view.setCardBackgroundColor(Color.WHITE);
studensToBeRemoved.remove(studentListItemList.get(pos).getId());
selected.set(pos,false);
}
if (studensToBeRemoved.size() > 0)
students.onMethodCallback(true);
else
students.onMethodCallback(false);
StringBuilder stringBuilder= new StringBuilder();
for(Integer integer : studensToBeRemoved)
stringBuilder.append(integer).append(", ");
Toast.makeText(mContext,String.valueOf(pos)+ ": "+stringBuilder.toString(),Toast.LENGTH_LONG).show();
return true;
};
#Override
public CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.student_list_row, null);
RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
view.setLayoutParams(lp);
CustomViewHolder viewHolder = new CustomViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(CustomViewHolder customViewHolder, int i) {
}
#Override
public void onBindViewHolder(CustomViewHolder customViewHolder, int i, List<Object> payload) {
if (payload.size() == 1) {
if ((boolean) payload.get(0)) {
customViewHolder.stud_card_view.setCardBackgroundColor(Color.GREEN);
studensToBeRemoved.add(studentListItemList.get(i).getId());
} else {
customViewHolder.stud_card_view.setCardBackgroundColor(Color.WHITE);
studensToBeRemoved.remove(studentListItemList.get(i).getId());
}
if (studensToBeRemoved.size() > 0)
students.onMethodCallback(true);
else
students.onMethodCallback(false);
} else {
Student studentListItem = studentListItemList.get(i);
Picasso.with(mContext).load(studentListItem.getImage())
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.into(customViewHolder.imageView);
customViewHolder.textView.setText(studentListItem.getName() + " " + studentListItem.getSurname());
customViewHolder.stud_card_view.setTag(customViewHolder);
customViewHolder.stud_card_view.setOnClickListener(clickListener);
customViewHolder.stud_card_view.setOnLongClickListener(longClickListener);
}
}
#Override
public int getItemCount() {
return (null != studentListItemList ? studentListItemList.size() : 0);
}
public void setFilter(ArrayList<Student> newList) {
studentListItemList = new ArrayList<>();
studentListItemList.addAll(newList);
notifyDataSetChanged();
}
public Set<Integer> getStudensToBeRemoved() {
return studensToBeRemoved;
}
public class CustomViewHolder extends RecyclerView.ViewHolder {
protected ImageView imageView;
protected TextView textView;
protected CardView stud_card_view;
public CustomViewHolder(View view) {
super(view);
this.imageView = view.findViewById(R.id.stud_list_pic);
this.textView = view.findViewById(R.id.stud_list_name);
this.stud_card_view = view.findViewById(R.id.stud_card_view);
}
}
}
PS. The payload check I have is because i have also implemented a selectAll() method (Which I call from the fragment host the RecyclerView) which uses the notifyItemRangeChanged() which is works properly.
Finally even the the 10th entry is also checked the studensToBeRemoved.add(studentListItemList.get(pos).getId()); for the 10th entry is never called actually. (The
Toast.makeText(mContext,String.valueOf(pos)+ ": "+stringBuilder.toString(),Toast.LENGTH_LONG).show();
in the end of onLongClickListener shows only the correct values every time ( e.g. when is select the first entry it shows "1: 1") although entry 10 is also selected). Finally if I select entry 10 the opposite happens (entry 1 is also selected).
Any Ideas whats going wrong? I can also provide some screenshots or more details if needed.
Thanks!
I managed to fix it. So here is the correct code in case someone wants to implement a simple RecyclerView with multi-selection. I hope it helps.
public class StudentRecyclerAdapter extends RecyclerView.Adapter<StudentRecyclerAdapter.CustomViewHolder> {
private List<Student> studentListItemList;
private Context mContext;
private StudentsFragment students;
private Set<Integer> studensToBeRemoved;
private List<Boolean> selected;
private View.OnClickListener clickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
CustomViewHolder holder = (CustomViewHolder) view.getTag();
int pos = holder.getAdapterPosition();
if (studensToBeRemoved.size() == 0) {
Student studentListItem = studentListItemList.get(pos);
Bundle extras = new Bundle();
extras.putInt("id", studentListItem.getId());
extras.putString("name", studentListItem.getName());
extras.putString("surname", studentListItem.getSurname());
extras.putLong("date", studentListItem.getLongBirthday());
extras.putString("photo", studentListItem.getImage());
Intent intent = new Intent(mContext, ProfileStudent.class);
intent.putExtras(extras);
mContext.startActivity(intent);
} else {
if (!selected.get(pos)) {
holder.stud_card_view.setCardBackgroundColor(Color.GREEN);
studensToBeRemoved.add(studentListItemList.get(pos).getId());
selected.set(pos, true);
} else {
holder.stud_card_view.setCardBackgroundColor(Color.WHITE);
studensToBeRemoved.remove(studentListItemList.get(pos).getId());
selected.set(pos, false);
}
if (studensToBeRemoved.size() > 0)
students.onMethodCallback(true);
else
students.onMethodCallback(false);
}
}
};
private View.OnLongClickListener longClickListener = view -> {
final CustomViewHolder holder = (CustomViewHolder) view.getTag();
int pos = holder.getAdapterPosition();
if (!selected.get(pos)) {
holder.stud_card_view.setCardBackgroundColor(Color.GREEN);
studensToBeRemoved.add(studentListItemList.get(pos).getId());
selected.set(pos, true);
} else {
holder.stud_card_view.setCardBackgroundColor(Color.WHITE);
studensToBeRemoved.remove(studentListItemList.get(pos).getId());
selected.set(pos, false);
}
if (studensToBeRemoved.size() > 0)
students.onMethodCallback(true);
else
students.onMethodCallback(false);
return true;
};
public StudentRecyclerAdapter(Context context, List<Student> studentListItemList, Students students) {
this.studentListItemList = studentListItemList;
this.mContext = context;
this.studensToBeRemoved = new HashSet<>();
this.students = students;
selected = new ArrayList<>();
for (int i = 0; i < studentListItemList.size(); i++)
selected.add(false);
}
#Override
public CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.student_list_row, null);
RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
view.setLayoutParams(lp);
CustomViewHolder viewHolder = new CustomViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(CustomViewHolder customViewHolder, int i) {
Student studentListItem = studentListItemList.get(i);
Picasso.with(mContext).load(studentListItem.getImage())
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.into(customViewHolder.imageView);
customViewHolder.textView.setText(studentListItem.getName() + " " + studentListItem.getSurname());
customViewHolder.stud_card_view.setTag(customViewHolder);
customViewHolder.edit_button.setTag(customViewHolder);
customViewHolder.stud_card_view.setOnClickListener(clickListener);
if (!selected.get(i))
customViewHolder.stud_card_view.setCardBackgroundColor(Color.WHITE);
else
customViewHolder.stud_card_view.setCardBackgroundColor(Color.GREEN);
customViewHolder.stud_card_view.setOnLongClickListener(longClickListener);
customViewHolder.stud_card_view.setOnClickListener(clickListener);
customViewHolder.edit_button.setOnClickListener(clickListener);
}
#Override
public int getItemCount() {
return (null != studentListItemList ? studentListItemList.size() : 0);
}
public void setFilter(ArrayList<Student> newList) {
studentListItemList = new ArrayList<>();
studentListItemList.addAll(newList);
notifyDataSetChanged();
}
public Set<Integer> getStudensToBeRemoved() {
return studensToBeRemoved;
}
public void setSelected(boolean b) {
if (b) {
for (int i = 0; i < selected.size(); i++) {
studensToBeRemoved.add(studentListItemList.get(i).getId());
selected.set(i, true);
}
students.onMethodCallback(true);
} else {
for (int i = 0; i < selected.size(); i++) {
selected.set(i, false);
studensToBeRemoved.remove(studentListItemList.get(i).getId());
}
students.onMethodCallback(false);
}
notifyItemRangeChanged(0, getItemCount());
}
public class CustomViewHolder extends RecyclerView.ViewHolder {
protected ImageView imageView;
protected TextView textView;
protected CardView stud_card_view;
protected ImageButton edit_button;
public CustomViewHolder(View view) {
super(view);
this.imageView = view.findViewById(R.id.stud_list_pic);
this.textView = view.findViewById(R.id.stud_list_name);
this.stud_card_view = view.findViewById(R.id.stud_card_view);
this.edit_button = view.findViewById(R.id.student_edit_btn);
edit_button.bringToFront();
}
}
}
The students.onMethodCallback() function informs the host fragment to show/hide the delete button. In order to select/deselect all element at once all you have to do is to call the setSelected(true/false) method.
After clicking on the list and setting the information on EditText & ImageView to update the database information
When I return to the previous one and return to the same activity again, the information on EditText & ImageView will not be deleted and will still be displayed.
How can I fix this problem?
code java class Manage:
MyDB db = new MyDB(getApplicationContext());
List<InfoData> data = db.fetchmaindata();
db.close();
adapter = new MyListAdapter(getApplicationContext(), postdata(data));
BtnLoadAll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
}
});
}
private View.OnClickListener onEditEmployeeSubmit = new View.OnClickListener() {
#Override
public void onClick(View view) {
if (EdtNAME.length()>0 && EdtFAMILY.length()>0 && EdtDESCRIPTION.length()>0) {
try {
myDB.open();
myDB.editEmployee(
Integer.parseInt(EdtID.getText().toString()),
EdtNAME.getText().toString(),
EdtFAMILY.getText().toString(),
EdtDESCRIPTION.getText().toString(),
ImgViewOrgsToByte(ImgViewOrgs)
);
//refreshList();
Toast.makeText(getApplicationContext(), "Information was updated!", Toast.LENGTH_SHORT).show();
EdtID.setText("");
EdtNAME.setText("");
EdtFAMILY.setText("");
EdtDESCRIPTION.setText("");
ImgViewOrgs.setImageResource(R.mipmap.ic_launcher);
myDB.close();
}catch (Exception e){
e.printStackTrace();
myDB.close();
}
}else{
Toast.makeText(getApplicationContext(),"Fill in all the information!",Toast.LENGTH_SHORT).show();
}
//btnCancel.performClick();
}
};
public void info(){
findViewById(R.id.EdtID).setVisibility(View.VISIBLE);
byte[] infoimage = Manage.image;
if (infoimage != null && infoimage.length > 0) {
Bitmap bitmap = BitmapFactory.decodeByteArray(infoimage, 0, infoimage.length);
EdtID.setText(Integer.toString(id));
EdtNAME.setText(title);
EdtFAMILY.setText(family);
EdtDESCRIPTION.setText(description);
ImgViewOrgs.setImageBitmap(bitmap);
}else{
return;
}
}
private List<Info> postdata(List<InfoData> db) {
List<Info> data = new ArrayList<>();
for (int i = 0; i < db.size(); i++) {
Info cur = new Info();
cur.name = db.get(i).getName();
cur.family = db.get(i).getFamily();
cur.description = db.get(i).getDescription();
cur.image = db.get(i).getImage();
data.add(cur);
}
return data;
}
code adapter:
public class MyListAdapter extends RecyclerView.Adapter<MyListAdapter.ViewHolder> {
private Context context;
private List<Info> data;
private LayoutInflater inflater;
public MyListAdapter(Context context, List<Info> data) {
inflater = LayoutInflater.from(context);
this.data = data;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_row_main, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Info cur = data.get(position);
holder.textView.setText(cur.name);
holder.textViewf.setText(cur.family);
byte[] infoimage = cur.image;
Bitmap bitmap = BitmapFactory.decodeByteArray(infoimage, 0, infoimage.length);
holder.imageView.setImageBitmap(bitmap);
Typeface font = Typeface.createFromAsset(context.getAssets(), "BKoodkBd.ttf");
holder.textView.setTypeface(font);
holder.textViewf.setTypeface(font);
}
#Override
public int getItemCount() {
return data.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private TextView textView,textViewf,textViewi;
private ImageView imageView;
public ViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.title_main);
textViewi = (TextView) itemView.findViewById(R.id.textViewi);
textViewf = (TextView) itemView.findViewById(R.id.title_mainf);
imageView = (ImageView) itemView.findViewById(R.id.imageView);
Button btnEdit = (Button) itemView.findViewById(R.id.btn_edit);
final Button btnDelete = (Button) itemView.findViewById(R.id.btn_delete);
//imageView = (ImageView) itemView.findViewById(R.id.iii);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Info curr = new Info();
curr = data.get(getPosition());
Manage.id = curr.id;
Manage.title = curr.name;
Manage.family = curr.family;
Manage.description = curr.description;
Manage.image = curr.image;
Manage.id = getPosition() + 1;
Intent intent = new Intent(context, Manage.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
});
}
}
}
Picture of the program