При прокрутке пользовательского ListView изменяется значение флажка
Что у меня: настраиваемый список с помощью Textviews и флажок.
Проблема: Предположим, что мой экран может отображать только 6 элементов списка во времени, а остальные невидимы.
Поэтому я проверил первый элемент в списке (позиция в позиции 0). Я прокручиваю вниз, чтобы увидеть весь список, когда я просматриваю первый элемент (позиция в позиции 0), флажок установлен правильно. Большой! Но теперь есть и новые элементы, которые тоже проверяются, например, элемент в позиции 8 (потому что когда я просматриваю список (для Rycicling), он становится новым элементом в позиции 0.. запомните, на моем экране показывается только 7 элементов). И он стал проверенным, но я его никогда не проверял!
Вопрос: Как я могу избежать этой проблемы? Я не хочу, чтобы флажок, который я не нажимал, менял свой статус.
Ниже моего адаптера для listView с функцией getView, где я реализовал setOnCheckedChangeListener:
public class NewQAAdapterSelectFriends extends BaseAdapter {
private LayoutInflater mInflater;
private Person[] data;
public NewQAAdapterSelectFriends(Context context) {
mInflater = LayoutInflater.from(context);
}
public void setData(Person[] data) {
this.data = data;
}
@Override
public int getCount() {
return data.length;
}
@Override
public Object getItem(int item) {
return data[item];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_select_friends, null);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.nameText=(TextView) convertView.findViewById(R.id.personName);
viewHolder.surnameText=(TextView) convertView.findViewById(R.id.personSurname);
viewHolder.contactImage=(ImageView) convertView.findViewById(R.id.personImage);
viewHolder.checkBox=(CheckBox)convertView.findViewById(R.id.checkBox);
convertView.setTag(viewHolder);
viewHolder.nameText.setTag(viewHolder.nameText);
viewHolder.nameText.setTag(viewHolder.surnameText);
viewHolder.contactImage.setTag(data[position]);
viewHolder.checkBox.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
Person element = (Person) viewHolder.checkBox.getTag();
if(isChecked){
element.setCheck(buttonView.isChecked());
//data[position].setCheck(true); //this is equivalent to previous line
}
else{
//to-do
}
}
});
viewHolder.checkBox.setTag(data[position]);
} else {
}
ViewHolder holder = (ViewHolder) convertView.getTag();
holder.nameText.setText(data[position].getName());
holder.surnameText.setText(data[position].getSurname());
holder.contactImage.setImageResource(data[position].getPhotoRes());
holder.contactImage.setScaleType(ScaleType.FIT_XY);
holder.checkBox.setChecked(data[position].isCheck());
return convertView;
}
static class ViewHolder {
TextView nameText;
TextView surnameText;
ImageView contactImage;
CheckBox checkBox;
}
}
Спасибо за ответы:)
EDIT: с предложением я изменил свой getView, как показано ниже:
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_select_friends, null);
viewHolder=new ViewHolder();
viewHolder.nameText=(TextView) convertView.findViewById(R.id.personName);
viewHolder.surnameText=(TextView) convertView.findViewById(R.id.personSurname);
viewHolder.contactImage=(ImageView) convertView.findViewById(R.id.personImage);
viewHolder.checkBox=(CheckBox)convertView.findViewById(R.id.checkBox);
convertView.setTag(viewHolder);
viewHolder.nameText.setTag(viewHolder.nameText);
viewHolder.nameText.setTag(viewHolder.surnameText);
viewHolder.contactImage.setTag(data[position]);
viewHolder.checkBox.setChecked(data[position].isCheck());
viewHolder.checkBox.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
if(viewHolder.checkBox.isChecked()==true)
data[position].setCheck(true);
else
data[position].setCheck(false);
}
});
}
else{
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.nameText.setText(data[position].getName());
viewHolder.surnameText.setText(data[position].getSurname());
viewHolder.contactImage.setImageResource(data[position].getPhotoRes());
viewHolder.contactImage.setScaleType(ScaleType.FIT_XY);
viewHolder.checkBox.setChecked(data[position].isCheck());
return convertView;
}
СИТУАЦИЯ ПОСЛЕ ИЗМЕНЕНИЯ: Теперь, если я проверю начальные элементы, которые отображаются на экране, а затем я прокручиваю список, их значение будет правильно сохранено. Но когда я просматриваю список и, например, я хочу проверить флажки последнего дерева в моем списке, то после того, как я прокручу их, они будут сняты... BUT WHY??????
SOLVED. Я решил проблему getView с помощью этого кода:
public class NewQAAdapterSelectFriends extends BaseAdapter {
private LayoutInflater mInflater;
private Person[] data;
boolean[] checkBoxState;
ViewHolder viewHolder;
public NewQAAdapterSelectFriends(Context context) {
mInflater = LayoutInflater.from(context);
}
public void setData(Person[] data) {
this.data = data;
checkBoxState=new boolean[data.length];
}
@Override
public int getCount() {
return data.length;
}
@Override
public Object getItem(int item) {
return data[item];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_select_friends, null);
viewHolder=new ViewHolder();
viewHolder.nameText=(TextView) convertView.findViewById(R.id.personName);
viewHolder.surnameText=(TextView) convertView.findViewById(R.id.personSurname);
viewHolder.contactImage=(ImageView) convertView.findViewById(R.id.personImage);
viewHolder.checkBox=(CheckBox)convertView.findViewById(R.id.checkBox);
convertView.setTag(viewHolder);
}
else{
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.nameText.setText(data[position].getName());
viewHolder.surnameText.setText(data[position].getSurname());
viewHolder.contactImage.setImageResource(data[position].getPhotoRes());
viewHolder.contactImage.setScaleType(ScaleType.FIT_XY);
viewHolder.checkBox.setChecked(checkBoxState[position]);
viewHolder.checkBox.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(((CheckBox)v).isChecked()){
checkBoxState[position]=true;
data[position].setCheck(true);
}else{
checkBoxState[position]=false;
data[position].setCheck(false);
}
}
});
return convertView;
}
static class ViewHolder {
TextView nameText;
TextView surnameText;
ImageView contactImage;
CheckBox checkBox;
}
}
Я видел этот учебник для моего getView: http://androidcocktail.blogspot.it/2012/04/adding-checkboxes-to-custom-listview-in.html
Ответы
Ответ 1
Я решил проблему, просто изменив "setOnCheckedChangeListener" на "setOnClickListener"
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
CheckBox cbItemChecklist = new CheckBox(context);
holder = new ViewHolder();
holder.cbItemChecklist = cbItemChecklist;
convertView = cbItemChecklist;
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final ItemChecklist itemChecklist = itensChecklist.get(position);
holder.cbItemChecklist.setText(itemChecklist
.getDescricaoItemChecklist());
holder.cbItemChecklist.setChecked(itemChecklist.isChecked());
holder.cbItemChecklist
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(itemChecklist.isChecked()){
itemChecklist.setChecked(false);
} else {
itemChecklist.setChecked(true);
}
}
});
return convertView;
}
Ответ 2
Проверьте код ниже -
public View getView(int position, View convertView, ViewGroup parent){
View view = convertView;
ViewHolder holder = new ViewHolder();
if(view == null){
view = inflater.inflate(R.layout.list_callcycle_blue, null);
holder.llContainer = (LinearLayout) view.findViewById(R.id.ll_container);
holder.lblLabel = (TextView) view.findViewById(R.id.txt_desc);
holder.cb = (CheckBox) view.findViewById(R.id.cb_store);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
final Object data = getItem(position);
holder.lblLabel.setText(data.getDescription());
holder.cb.setTag(position);
holder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int position = (Integer) buttonView.getTag();
objects.get(position).setChecked(buttonView.isChecked());
}
});
holder.cb.setChecked(isChecked(position));
return view;
}
Всегда помните, используйте change holder.cb.setOnCheckedChangeListener()
т.е. любой прослушиватель перед установкой данных, в нашем случае это holder.cb.setChecked()
Причина:. Когда мы прокручиваем, listview будет перерабатывать представления, поэтому, если setchecked используется перед слушателями, тогда он будет выбирать значения на основе старого слушателя. И если мы установим его после слушателя, то он примет последние значения
Ответ 3
Вы не рассматривали случай convertView!= null, это то, что происходит при прокрутке вверх и вниз. Вы должны реализовать способ переработать convertView (лучше) или просто проигнорировать его и дать новый вид назад в этом случае (хуже).
Ответ 4
Вы устанавливаете тег флажка только в том случае, если convertview имеет значение null. Это происходит только для первого экрана записей. Когда пользователь прокручивается вниз, предыдущие convertviews перерабатываются. Таким образом, ваши флажки имеют более старые элементы данных в качестве их тегов.
Ваш проверочный прослушиватель изменений должен выглядеть так:
new CompoundButton.OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked)
{
Person element = (Person) viewHolder.checkBox.getTag();
data[position].setCheck(isChecked);
if(isChecked)
{
// do your stuff
}
else
{
//to-do
}
}
}
Ответ 5
проверить этот код
public class NewQAAdapterSelectFriends extends BaseAdapter {
private LayoutInflater mInflater;
private Person[] data;
ArrayList<String> checkedItem=new ArrayList<String>();
public NewQAAdapterSelectFriends(Context context) {
mInflater = LayoutInflater.from(context);
}
public void setData(Person[] data) {
this.data = data;
}
@Override
public int getCount() {
return data.length;
}
@Override
public Object getItem(int item) {
return data[item];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_select_friends, null);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.nameText=(TextView) convertView.findViewById(R.id.personName);
viewHolder.surnameText=(TextView) convertView.findViewById(R.id.personSurname);
viewHolder.contactImage=(ImageView) convertView.findViewById(R.id.personImage);
viewHolder.checkBox=(CheckBox)convertView.findViewById(R.id.checkBox);
convertView.setTag(viewHolder);
viewHolder.nameText.setTag(viewHolder.nameText);
viewHolder.nameText.setTag(viewHolder.surnameText);
viewHolder.contactImage.setTag(data[position]);
viewHolder.checkBox.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
Person element = (Person) viewHolder.checkBox.getTag();
if(isChecked){
getCheckList(data[position],isChecked);
}
else{
getCheckList(data[position],isChecked);
}
}
});
viewHolder.checkBox.setTag(data[position]);
}
ViewHolder holder = (ViewHolder) convertView.getTag();
holder.nameText.setText(data[position].getName());
holder.surnameText.setText(data[position].getSurname());
holder.contactImage.setImageResource(data[position].getPhotoRes());
holder.contactImage.setScaleType(ScaleType.FIT_XY);
if(checkedItem != null && checkedItem.contains(data[position])){
viewHolder.checkBox.setChecked(true);
}else
{
viewHolder.checkBox.setChecked(false);
}
return convertView;
}
static class ViewHolder {
TextView nameText;
TextView surnameText;
ImageView contactImage;
CheckBox checkBox;
}
public void getCheckList(String value,boolean status){
if(!checkedItem.contains(value) && status)
{
checkedItem.add(value);
}
if(checkedItem.contains(value) && !status)
{
checkedItem.remove(value);
}
}
}
Ответ 6
У меня есть хорошая альтернатива для CheckBox. Вы можете использовать android:state_activated="true"
для строк ListView. Он не теряет своего состояния и не требует большого количества кода.
Вот как работает
- создайте
active_row.xml
в своей папке
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true"
android:drawable="@android:color/darker_gray"/>
</selector>
- Установите для фона своего строя в качестве
android:background="@drawable/active_row.xml"
- И, наконец, добавьте это в свой ListView
android:choiceMode="multipleChoice"
Вы можете проверить состояние каждой строки следующим образом
View v;
LinearLayout yourRowLayout;
for (int i = 0; i < yourListView.getChildCount(); i++) {
v = yourListView.getChildAt(i);
yourRowLayout = (LinearLayout) v.findViewById(R.id. yourRowLayout);
if(yourRowLayout.isActivated()){.... } }
Ответ 7
Используйте setTag() в адаптере. Здесь вы можете увидеть пример Пример listview с проблемой прокрутки флажка
Ответ 8
Вам необходимо обновить адаптер, чтобы сохранить значение флажка
holder.chk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CheckBox c = (CheckBox)v.findViewById(R.id.chkID);
Boolean chk = c.isChecked();
Integer pos = (Integer)v.getTag();
itemList.get(pos).setChk(chk);
}
});
Для получения дополнительной справки вы можете отправить это сообщение в Сохранить значение флажка