Reputation: 29
I have implemented a RecyclerView
to display a list of Contacts (200 approximately). A user can select multiple items. The selection make visible a tick to indicate to the user the selected contacts. However, when the user select a item, the tick appears on several non-selected items. The frequency of occurrence is the same.
Could you tell me what I am doing wrong. Thanks !
Here is my implementation :
I have an activity called NewAppointmentActivity which contains a fragment called NewAppointmentPhoneContactsFragment. This fragment is a RecyclerView :
File fragment_new_appointment_phone_contacts.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/list"
android:name="com.example.geoappointment.fragments.NewAppointmentPhoneContactsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context=".fragments.NewAppointmentPhoneContactsFragment"
tools:listitem="@layout/fragment_contact_item" />
File NewAppointmentPhoneContactsFragment.java
public class NewAppointmentPhoneContactsFragment extends Fragment {
private static final String TAG = "NewAppointmentPhoneCont";
private NewAppointmentActivityViewModel viewModel;
public NewAppointmentPhoneContactsFragment() {}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(NewAppointmentActivityViewModel.class);
new LoadContactsFromPhoneAsyncTask(
requireActivity().getContentResolver(),
requireActivity().getApplicationContext(),
new AsyncTaskCallback<ArrayList<Contact>>() {
@Override
public void onPostExecuteCallback(ArrayList<Contact> param) {
if (viewModel != null) {
viewModel.setPhoneContacts(param);
}
}
}).execute();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_new_appointment_phone_contacts, container, false);
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
recyclerView.setLayoutManager(new LinearLayoutManager(context));
viewModel.getPhoneContacts().observe(getViewLifecycleOwner(), contacts -> {
recyclerView.setAdapter(new MyContactListRecyclerViewAdapter(contacts));
});
}
return view;
}
}
When the AsynTask is done, it notifies the model which sets the adapter to the RecyclerView (it works fine).
File MyContactListRecyclerViewAdapter.java
public class MyContactListRecyclerViewAdapter extends RecyclerView.Adapter<MyContactListRecyclerViewAdapter.ViewHolder> {
private static final String TAG = "MyContactListRecyclerVi";
private final ArrayList<Contact> mValues;
private final ArrayList<Contact> mSelectedValues;
public MyContactListRecyclerViewAdapter(ArrayList<Contact> items) {
mValues = items;
mSelectedValues = new ArrayList<>();
}
@NotNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater
.from(parent.getContext())
.inflate(R.layout.fragment_contact_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.mNameTextView.setText(holder.mItem.getName());
holder.mPhoneNumberView.setText(holder.mItem.getPhoneNumber());
if (mSelectedValues.contains(holder.mItem)) {
holder.tickImageView.setVisibility(View.VISIBLE);
}
holder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClick(holder, position);
}
});
}
@Override
public int getItemCount() {
return mValues.size();
}
public void onItemClick(final ViewHolder holder, int position) {
// Récupération du contact sélectionné
final Contact contact = mValues.get(position);
Log.d(TAG, contact.toString());
// Si le contact n'est pas nul
if (contact != null) {
// Si le contact n'est pas déjà sélectionné
if (!mSelectedValues.contains(contact)) {
// Ajout du contact dans la liste des contacts sélectionnés
mSelectedValues.add(contact);
// Démasque la tick de sélection
holder.tickImageView.setVisibility(View.VISIBLE);
} else {
// Retire le contact de la liste des contacts sélectionnés
mSelectedValues.remove(contact);
// Masque la tick de sélection
holder.tickImageView.setVisibility(View.INVISIBLE);
}
}
Log.d(TAG, mSelectedValues.toString());
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
public final TextView mNameTextView;
public final TextView mPhoneNumberView;
public final ImageView tickImageView;
public Contact mItem;
public ViewHolder(View view) {
super(view);
mView = view;
mNameTextView = (TextView) view.findViewById(R.id.contactItemNameTextView);
mPhoneNumberView = (TextView) view.findViewById(R.id.contactItemPhoneNumber);
tickImageView = (ImageView) view.findViewById(R.id.contactItemTickImageView);
if (mItem != null) {
if (mSelectedValues.contains(mItem)) {
tickImageView.setVisibility(View.VISIBLE);
}
}
}
}
}
Upvotes: 1
Views: 1396
Reputation: 40898
Add the else
branch in the onBindViewHolder
to avoid the repetition of the visible ticked items while recycling views
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.mNameTextView.setText(holder.mItem.getName());
holder.mPhoneNumberView.setText(holder.mItem.getPhoneNumber());
if (mSelectedValues.contains(holder.mItem)) {
holder.tickImageView.setVisibility(View.VISIBLE);
} else {
holder.tickImageView.setVisibility(View.GONE);
}
holder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClick(holder, position);
}
});
}
Upvotes: 2