【问题标题】:Checkbox lost checked value when scroll my custom ListView滚动我的自定义 ListView 时复选框丢失了选中值
【发布时间】:2012-07-06 07:19:34
【问题描述】:

我所拥有的: 带有 Textview 和复选框的自定义列表视图。
问题:如果我检查屏幕上显示的初始项目然后滚动列表,当我返回列表顶部(向上滚动)时,它们的值被正确保存。但是,如果我滚动列表,例如我想检查列表的最后一个树复选框,那么如果我向上和向下滚动它们就会被取消选中....为什么?**
我在其他论坛和 stackoverflow 上都看到过针对同一问题的各种解决方案,但问题仍然存在。
在我认为可以的 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;
}

已解决:我通过以下代码解决了 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

【问题讨论】:

    标签: android listview checkbox


    【解决方案1】:

    当你甚至在方法中使用new 创建一个类时,你已经为你的变量创建了一个不同的范围。换句话说:

    // Creating a new scope here -->       vvv
    viewHolder.checkBox.setOnClickListener(new OnClickListener() {
        // Any variables not visible to the entire class, won't work properly in here! 
        //  (variables like: viewHolder, position, possibly data) 
        public void onClick(View arg0) {  
            if(viewHolder.checkBox.isChecked()==true)   
                data[position].setCheck(true);  
            else  
                data[position].setCheck(false);  
        }  
    }); // Exited "new class'" scope
    

    position 与您认为的 position 不同。尝试保存position 的值,以便再次引用它:

    viewHolder.checkBox = (CheckBox) convertView.findViewById(R.id.checkBox);
    viewHolder.position = position;
    convertView.setTag(viewHolder);
    ...
    
    viewHolder.checkBox.setOnClickListener(new OnClickListener() {  
        public void onClick(View view) {
            ViewHolder viewHolder = (ViewHolder) view.getTag();
            data[viewHolder.position].setCheck(viewHolder.checkBox.isChecked());   
        }  
    }); 
    

    由于您正在处理 CheckBox,但我建议您使用 OnCheckedChangeListener

    但如果您更改ChoiceMode,则可能不需要为 ListView 行定义任何侦听器:

    listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    

    【讨论】:

    • 您好,感谢您的回答。我按照你的建议更改了我的代码,但是现在当我点击一个复选框时,我在 data[viewHolder.position].setCheck(viewHolder.checkBox.isChecked()); 行上有一个 NullPointerException为什么???
    • 使用调试器或 logcat 语句来确认 viewHolder 不为空,有一个 position,有一个 CheckBox 并且 data[viewHolder.position] 也是有效的。但我想我知道我在哪里搞砸了,试试这个:ViewHolder viewHolder = (ViewHolder) ((View) view.getParent()).getTag();;
    • Sam,我按照你的建议再次更改了我的代码,但是使用 Log.i(String,String) 我看到当我点击一个复选框时 viewHolder.position 是正确的(我的 dislapy 可以显示一次有 8 个项目,所以 viewHolder.position 的范围在 0,1,2,3..7 和下一个元素重新启动为 0,1,..(用于回收)。使用日志我检查数据 [viewHolder。 position].getNamePerson() 也适用于每个元素。列表顶部的前 8 个元素(0,1,2..7)有效(滚动时保存它们的复选框状态)。问题仍然存在下一个前 8 个之后的元素,它们永远不会保持其价值。
    • 例如,如果我只检查列表的最后一个元素(索引为 7 的元素),当我向上滚动到列表顶部时,我会得到以下结果:1)我检查的元素(列表的最后一个)返回未检查,并且现在显示在索引 7 的列表顶部的元素被检查!!!!所以似乎另一个元素上的onclick(列表的前8个之后)改变了前8个的值。所以如果我滚动列表末尾的第五个元素,列表顶部的第五个元素是检查等等...请帮帮我!!!
    • 对不起,我的意思是,....所以如果我检查列表末尾的第五个元素,则检查列表顶部的第五个元素,依此类推。
    【解决方案2】:

    还在viewHolder.checkBox.setOnCheckedChangedListenerviewHolder.checkBox.setOnClickListener 之后添加viewHolder.checkBox.setChecked(checkBoxState[position]);

    因为滚动后,当listview回收它的视图时,它会在旧监听器的基础上使用setChecked,这在某些情况下可能会丢失值。

    所以这是流程 -

    getView()
    ---
    ---
    ---
    viewHolder.checkBox.setOnCheckedChangedListener()
    ---
    ---
    ---
    viewHolder.checkBox.setChecked()
    

    【讨论】:

      【解决方案3】:
      public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>
      {
          boolean[] checkBoxState;
      
      
          public MyAdapter(Context context, ArrayList<String> list ) {
              checkBoxState = new boolean[list.size()];
          }
      
          @Override
          public void onBindViewHolder( final ViewHolder holder,  final int position) {
              holder.checkBox.setTag(position); 
      
                  holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
                      @Override
                      public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                          if(compoundButton.isPressed()) // check whether user pressed the checkbox.
                          {
                              int getPosition = (Integer) compoundButton.getTag();  // Here we get the position that we have set for the checkbox using setTag.
                              checkBoxState[getPosition] = compoundButton.isChecked(); // Set the value of checkbox to maintain its state.
                          }
                      }
                  });
      
                  holder.checkBox.setChecked(checkBoxState[position]);
          }   
      
          public class ViewHolder extends RecyclerView.ViewHolder  {
              public CheckBox checkBox;
              public ViewHolder(View v,MainActivity mainActivity) {
                  super(v);
                  checkBox= (CheckBox) v.findViewById(R.id.checkBox);
              }
          }
      
          @Override
          public int getItemCount() {
              return list.size();
          }
      }
      

      滚动时,可能会自动调用 onCheckedChanged() 方法。因此,您可能需要 isPressed() 方法来检查用户是否按下了复选框。我的适配器类如上所示。请注意,当您向列表中添加新元素时,您应该更新布尔数组 checkBoxState,例如,当您向列表中添加元素时,您应该调用以下代码:

      checkBoxState = new boolean[list.size()+1];
      

      或者使用一个数组列表来保存布尔变量并将元素添加到这个数组列表中,无论你想要什么。

      我还为此解决方案实施了一个项目。你可以在这里下载 : https://www.dropbox.com/s/ssm58w62gw32i29/recyclerView_checkbox_highlight.zip?dl=0

      截图如下:

      【讨论】:

      • 问题是关于ListViewclass,而不是RecyclerView
      • recyclerView也出现同样的问题。通过为 listView 实现一个自定义适配器类,可以对 listView 执行相同的解决方案。
      猜你喜欢
      • 2012-07-05
      • 1970-01-01
      • 2021-03-11
      • 2018-04-20
      • 2015-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多