Вам не нужно использовать SharedPreferences
для сохранения состояний флажков. Вы можете просто использовать метод setChecked
внутри своего класса ListOfNames
.
Кроме того, вы получаете эту ошибку при добавлении нового элемента, потому что метод onCheckedChanged
слушателя CheckBox
получает без необходимости вызывается , когда вы вызываете notifyDataSetChanged
. Идея заключается в том, что когда вы уведомляете адаптер, он перерабатывает , другими словами, он воссоздает Представления поэтому, если флажки были отмечены, они снова стали не отмеченными, он также может вызвать onCheckedChanged
, изменяющий предыдущее значение. (Примечание: я вспомнил, что термин переработать отличается от воссоздать )
Один из способов справиться с этим - использовать вместо него OnClickListener
и получить CheckBox
оттуда и посмотрите его значение.
Итак, вместо :
holder.mCheckedBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// ...
}
});
Измените его на это:
holder.mCheckedBox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CheckBox checkBox = (CheckBox) view;
boolean isChecked = checkBox.isChecked();
// ...
}
});
Это должно решить, что это странное явление не произошло.
Вернитесь к использованию SharedPreferences
, как я уже предлагал, используйте метод setChecked
вашего ListOfNames
класса (см. Код ниже) . Затем удалите все, что связано с SharedPreferences
внутри вашего адаптера. И поскольку вы не добавляете новые элементы и не удаляете какие-либо флажки, которые вы устанавливаете, он не будет сохранен, пока пользователь не нажмет кнопку сохранения в вашем меню. В котором вы можете просто использовать свой saveData
метод вашего StudentListActivity
для кнопки сохранения.
Также удалите интерфейс ItemClickListener
и implements View.OnClickListener
внутри вашего ViewHolder
. Это довольно избыточно.
Ваш AdapterForStudentList
теперь должен выглядеть так:
public class AdapterForStudentList extends RecyclerView.Adapter<AdapterForStudentList.StudentViewHolder> {
private Context context;
private ArrayList<ListOfNames> mListOfNames;
public static class StudentViewHolder extends RecyclerView.ViewHolder {
public TextView studentName, attendence, percentage;
CheckBox mCheckedBox;
public StudentViewHolder(@NonNull View itemView) {
super(itemView);
studentName = itemView.findViewById(R.id.studentName);
attendence = itemView.findViewById(R.id.attendence);
percentage = itemView.findViewById(R.id.percentage);
mCheckedBox = itemView.findViewById(R.id.mCheckBox);
}
}
public AdapterForStudentList(Context context, ArrayList<ListOfNames> listOfNames) {
this.context = context;
mListOfNames = listOfNames;
}
@NonNull
@Override
public StudentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.check_box_cardview, parent, false);
return new StudentViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull StudentViewHolder holder, final int position) {
final ListOfNames currentItems = mListOfNames.get(position);
holder.studentName.setText(currentItems.getStudentName());
holder.attendence.setText(currentItems.getAttendent());
holder.percentage.setText(currentItems.getPercetage());
holder.mCheckedBox.setChecked(currentItems.getChecked());
holder.mCheckedBox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CheckBox checkBox = (CheckBox) view;
boolean isChecked = checkBox.isChecked();
currentItems.setChecked(isChecked);
}
});
}
@Override
public int getItemCount() {
return mListOfNames.size();
}
}
Примечание: Сделайте не забудьте реализовать ограничения, которые не позволят пользователю добавлять или удалять элементы. Дело не в том, что будет грандиозное исключение, скорее оно сделает вашу функцию сохранения бесполезной .
Кроме того, у меня создалось впечатление, что вы хотели иметь возможность добавлять / удалять элементы, пока также возможность устанавливать / снимать флажки. Решение, которое я придумал, - это для создания другого списка, который будет содержать позиции отмеченных флажков , а затем использовать его, когда пользователь нажимает кнопку сохранения. (См. Обновленный пост ниже) Это должно дать вам как возможность добавлять / удалять, так и отмечать / снимать отметки, при этом еще не сохраняя проверки, пока пользователь не сделает это. (Если вы этого хотите, я могу сделать код и поместить его сюда)
И последнее, если вы поставите ограничения, я вижу без проблем, используя вместо OnCheckedChangedListener
реализации, которую я сделал выше, потому что я не чувствую, что вы будете звонить notifyDataSetChanged
где-либо еще, кроме того, когда элемент был добавлен / удален.
Надеюсь, я кое-что прояснил.
Обновление: реализовать как возможность изменения списка, так и возможность сохранять состояния флажков только при действии пользователя
Решение, которое я дал вам ранее для создания списка для размещения позиций отмеченных элементов, - это на самом деле не очень хорошая идея . Что если убрать из списка? Или, что еще хуже, что, если мы отсортируем список?
Я думаю, что это лучший подход: создать временную переменную, которая хранит временное состояние флажка и передает его значение, только когда пользователь нажимает при сохранении. Временные переменные не сериализуются / не включаются в строку json, когда мы конвертируем ее с помощью Gson
.
Итак, внутри вашего класса ListOfNames
я предлагаю создать этот переходный переменная. И используйте это для временной проверки / снятия отметки. Сохраняйте его в SharedPreferences
только тогда, когда пользователь действительно этого хочет.
Ваш ListOfNames
должен выглядеть так:
public class ListOfNames {
// variables...
// Create a transient variable to hold the temporary state of the check box
private transient boolean tempChecked;
// constructor and other methods...
// Couple of methods for operations on temporary checks and actual checks
public void setTemporaryChecked(boolean checked) {
tempChecked = checked;
}
public boolean getTemporaryChecked() {
return tempChecked;
}
public void checkedToTemporaryChecked() {
tempChecked = checked;
}
public void temporaryCheckedToChecked() {
checked = tempChecked;
}
}
Внутри слушателя для CheckBox
в onBindViewHolder
:
// Get the temporary checked value instead of the actual one
// (Because you know, RecyclerView recycles views)
holder.mCheckedBox.setChecked(currentItems.getTemporaryChecked());
holder.mCheckedBox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CheckBox checkBox = (CheckBox) view;
boolean isChecked = checkBox.isChecked();
// Set the temporary checked instead
currentItems.setTemporaryChecked(isChecked);
}
});
Внутри вашего loadData
метода:
public void loadData() {
// ...
// Set our temporary checks
// Important because transient variables don't get included in Json string
for (ListOfNames current: listOfNames)
current.checkedToTemporaryChecked();
}
Наконец внутри вашего сохранить в меню код logi c ( НЕ ВНУТРИ ваш saveData
метод):
case R.id.saveButton:
// ...
// Convert temporary checks to actual checks
for (ListOfNames current: listOfNames)
current.temporaryCheckedToChecked();
saveData();
return true;
// ...
И готово! Вы должны иметь возможность добавлять, удалять или даже сортировать список (если вы хотите добавить эту функцию позже), не затрагивая флажки, а также сохранять значения этих флажков только тогда, когда это делает пользователь.