为什么当带有 CheckBox 的 ListView 行离开屏幕时不更新数据模型?

Why data model is not updated when ListView row with a CheckBox gets off-screen?

虽然这是我想要的行为,但有人可以解释一下为什么会这样吗?

我有一个 ListView,其中包含一个 CheckBox 和一个 TextView 的自定义行。这是行布局代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="horizontal">

    <CheckBox
        android:id="@+id/shop_list_checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/shop_list_textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Sample text"
        />

</LinearLayout>

然后我有了这个简单的数据模型(省略了一些方法和字段),基本上是一个有名称和选择状态的商店:

public class ShopListItem {

    //name of the shop
    private String name;

    //shop selection state
    private boolean isSelected;

    public ShopListItem(String shopKey, String name, boolean isSelected) {
        this.shopKey = shopKey;
        this.name = name;
        this.isSelected = isSelected;
    }

    public boolean isSelected() {
        return isSelected;
    }

    public void setSelected(boolean selectionState) {
        this.isSelected = selectionState;
    }
}

这是我自定义的相关代码 ArrayAdapter:

public View getView(int position, View convertView, @NonNull ViewGroup parent) {

        // Temporary view to be returned as converterView
        View view;

        // Checking if view is reused or not
        if (convertView == null) { //new view is generated

            // Inflate new view and associate its views with viewHodler
            view = inflater.inflate(R.layout.row_shop_list, parent, false);
            final ViewHolder viewHolder = new ViewHolder();
            viewHolder.shopNameTextView = (TextView) view.findViewById(R.id.shop_list_textView);
            viewHolder.checkBox = (CheckBox) view.findViewById(R.id.shop_list_checkbox);

            // Asign check change listener
            viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    Log.i("i", "isChecked value is: "+isChecked+", buttonView.isChecked() value is: "+buttonView.isChecked());

                    // Get reference to the shop (data model) of checked row
                    ShopListItem shop = (ShopListItem) viewHolder.checkBox.getTag();

                    // Update selection state in data model
                    shop.setSelected(buttonView.isChecked());

                    listSelections(shopsArrayList);

                }
            });


            // Store viewHolder inside temporary view
            view.setTag(viewHolder);

            // Store reference to a shop (data model) of this row in the checkbox reference
            viewHolder.checkBox.setTag(shopsArrayList.get(position));

        } else {  //existing view reused

            // Assign view from adapter to temporary view
            view = convertView;

            // Get viewholder from view which was stored while generating new view
            ViewHolder storedHolder = (ViewHolder) view.getTag();

            //Store reference to a shop (data model) of this row in the checkbox reference
            storedHolder.checkBox.setTag(shopsArrayList.get(position));

        }

        // Get viewholder from view (from new view or converview)
        ViewHolder holder = (ViewHolder) view.getTag();

        // Populate holder references with data from shops array list
        holder.shopNameTextView.setText(shopsArrayList.get(position).getName());
        holder.checkBox.setChecked(shopsArrayList.get(position).isSelected());

        //return view
        return view;

    }

谜底在于OnCheckedChangeListener。每当我单击第一行的复选框时,侦听器都会收到正确的状态并更新商店数组列表的第一项(为真或假)。但是,如果我选中第一个复选框并向下滚动列表,一旦第一行离开屏幕,就会以状态 false 调用 OnCheckedChangeListener,但商店数组列表的第一项不会更新至 false。为什么会这样?为什么它只在单击框时更新,而不是在隐藏调用时不更新?

在您的数据列表中添加一个标志以维护选中的数据项>

 public boolean isSelected() {
  return selected;
 }
 public void setSelected(boolean selected) {
  this.selected = selected;
 }

并在 Adapter 的 getView() 中为复选框添加代码

 @Override
  public View getView(int position, View convertView, ViewGroup parent) {

   ViewHolder holder = null;
   Log.v("ConvertView", String.valueOf(position));

   if (convertView == null) {
   LayoutInflater vi = (LayoutInflater)getSystemService(
     Context.LAYOUT_INFLATER_SERVICE);
   convertView = vi.inflate(R.layout.country_info, null);

   holder = new ViewHolder();
   holder.code = (TextView) convertView.findViewById(R.id.code);
   holder.name = (CheckBox) convertView.findViewById(R.id.checkBox1);
   convertView.setTag(holder);

    holder.name.setOnClickListener( new View.OnClickListener() {  
     public void onClick(View v) {  
      CheckBox cb = (CheckBox) v ;  
      Country country = (Country) cb.getTag();  
      Toast.makeText(getApplicationContext(),
       "Clicked on Checkbox: " + cb.getText() +
       " is " + cb.isChecked(), 
       Toast.LENGTH_LONG).show();
      country.setSelected(cb.isChecked());
     }  
    });  
   } 
   else {
    holder = (ViewHolder) convertView.getTag();
   }

   Country country = countryList.get(position);
   holder.code.setText(" (" +  country.getCode() + ")");
   holder.name.setText(country.getName());
   holder.name.setChecked(country.isSelected());
   holder.name.setTag(country);

   return convertView;

  }

更多信息请见下文Link:

http://www.mysamplecode.com/2012/07/android-listview-checkbox-example.html

当您滚动列表时,第一项离开屏幕。当项目关闭时,将调用 getView 并使用视图。您需要保持该特定对象的状态才能正确显示在 UI 级别

只要您点击复选框,OnCheckedChangeListener 就会被激活。当您向下或向上滚动时,将调用 OTHER OnCheckedChangeListener 以显示屏幕上的 NEW ROW。隐藏线的OnCheckedChangeListener没有被调用

在你的情况下,我想,你正在将新行的复选框值显示到屏幕上,这让你感到困惑。尝试区分 OnCheckedChangeListener's 行中引用的商店对象:

ShopListItem shop = (ShopListItem) viewHolder.checkBox.getTag();