【问题标题】:Android: Problem With ListViews and CheckBoxesAndroid:ListViews 和 CheckBoxes 的问题
【发布时间】:2010-09-07 02:38:10
【问题描述】:

我有一个 ListView,在每个列表项中我都有一些 TextView 和一个 CheckBox。当我检查 CheckBox 并且我的 onCheckedChangeListener 触发时,一切正常。但是,一旦选中一个复选框,就会随机选中其他复选框。这是一个例子。

如果我点击第一个 CheckBox: 8 被检查。 15 被检查。 21 被检查。 27 被检查。 33 被检查。 41 被检查。 然后,如果我一直向上滚动,直到 6 点才会检查。下一个是 13 点。

基本上……这是怎么回事?

【问题讨论】:

    标签: java android listview checkbox


    【解决方案1】:

    您似乎正在重用在您实现的getView() 方法上传递的convertView。

    Android 将尝试对 ListView 中的不同项目使用相同的视图。您将需要 (1) 手动取消选中/选中返回项目中的复选框(在返回 getView 之前始终调用 setChecked 或 (2) 不使用 convertView,而是从 getView 返回一个新视图。

    (1)是推荐的,我认为。

    【讨论】:

    • 好的,所以我选择了选项一。就在我在 getView 中返回视图之前,我执行“checkBox.setChecked(false);”。但是,现在,当我选中一个复选框,向下滚动并向上滚动时,它不再被选中。我知道问题在于我的实现,而不是你的概念。我做错了什么?
    • 当用户选中复选框时,您需要在代码中的某处存储该项目的复选框已被选中。例如,如果您的 ListView 中有 40 个项目,则您可能有一个布尔数组来存储是否选中了第 i 个复选框。然后在getView 上,执行 checkBox.setChecked(booleanArray[position])。
    • 可能是因为太晚了,但我不知道如何实际记录位置。在 getView 方法中我可以获得位置,但在我需要实际存储位置的 OnClick 中,我不再可以访问它。
    • 在 getView 中,使用 setTag() developer.android.com/reference/android/view/… 存储位置,稍后在 onClick 中使用 getTag() 检索它。
    • 永远不要使用 (2)。 convertView 可以正确回收视图。如果您每次对 listView 中的每个项目都使用一个新视图,那么您最终会消耗大量内存以产生悲惨的结果。假设有一个包含 500-1000 个视图的布局。
    【解决方案2】:

    适合我

    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
    
            final ViewHolder holder;
            final Season season = (Season) getGroup(groupPosition);
            if (convertView == null) {
                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = vi.inflate(R.layout.season, parent, false);
                holder = new ViewHolder();
                holder.title = (TextView) convertView.findViewById(R.id.season_title);
                holder.checkBox = (CheckBox) convertView.findViewById(R.id.season_check_box);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
    
            holder.title.setText(season.getTitle());
            holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    season.setChecked(isChecked);
                    adapter.notifyDataSetChanged();
                }
            });
    
            holder.checkBox.setChecked(season.isChecked()); // position is important! Must be before return statement!
            return convertView;
        }
    
        protected class ViewHolder {
            protected TextView title;
            protected CheckBox checkBox;
        }
    

    【讨论】:

    • 我不知道什么季节,但对我来说这似乎很有效。对于其他有问题的人(我个人对复选框没有问题,但删除线效果随机出现在项目上)。所以:最终的 ViewHolder 持有者;和 setOnCheckedChangeListener 是让事情正常工作的两件事。希望这对其他人有帮助!谢谢你乔治!
    • 你很棒的 yarr..我做了很多研发。这是最好的解决方案
    • @Georgy Gobozov 请同时查看此问题stackoverflow.com/questions/44561788/…
    【解决方案3】:

    我的切换视图也有同样的问题。经过大量搜索和阅读,我发现这个问题是Android推广的视图优化的结果。它尝试重用视图对象。因此,基本上,当您单击开关或复选框时,您也会在另一个视图中触发更改回调。优化确实有效,但如果你不注意,或者只是不知道行为(比如我),就会发生一些非常奇怪的结果。

    无论如何,我找到了这个简单的解决方案:

    最佳实践是重用通过 getView() 方法传递的 convertView。通过这种方式,您可以优化列表视图使用的 Ram 数量。因此,我将视图存储在 ViewHolder 中并编写了 onCheckedChanged 事件。诀窍是,在设置是否选中开关(在您的情况下,选中复选框)并定义 CheckedChange 侦听器之前,我只需使用 null 重置回调,这样可以确保不会触发另一个开关的事件。

    这里是sn-p:

    ...
    viewHolder.switchView.setOnCheckedChangeListener(null);
    viewHolder.switchView.setChecked(myObject.getStatus() > 0);
    ...
    viewHolder.switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    ...
                }
            });
    

    myObject 是一个存储有光标数据的最终对象。

    而且无论开关是否被检查,你都必须调用 setChecked 方法。

    希望对某人有所帮助!

    【讨论】:

      【解决方案4】:

      我也遇到过类似的问题,所以经过大量阅读后,我解决了这个问题:

      @Override
          public View getView(int position, View convertView, ViewGroup parent) {
              ViewHolder holder = null;
              if (convertView == null) {
                  convertView = mInflater.inflate(R.layout.listview, null);
                  holder = new ViewHolder();
                  holder.nameView = (TextView)convertView.findViewById(R.id.textView1);
                  holder.numberView = (TextView)convertView.findViewById(R.id.textView2);
                  holder.cb = (CheckBox)convertView.findViewById(R.id.checkBox1);
                  convertView.setTag(holder);                
              } else {
                  holder = (ViewHolder)convertView.getTag();
              }
              holder.nameView.setText(mData.get(position).toString());
              holder.numberView.setText(mNumber.get(position).toString());
              holder.cb.setChecked(false);
              holder.cb.setTag(position);
      
      
      
              if(selected.indexOf(mNumber.get(position).toString()) >= 0) // Check whether this row checkbox was checked, for that we previously stored the textview text in selected variable
              {
      
              holder.cb.setChecked(true);
              }
      
              return convertView;
          }
      
      }
      

      这是我在 getView() 上所做的,我取消选中所有复选框并再次手动检查那些我需要根据它对应的 textview 检查的复选框。因此,如果用户在选中第一个复选框后向下滚动,则视图中的所有复选框都将被取消选中,如果他再次向上滚动,那么所有复选框也将被取消选中,但他之前单击的复选框将再次被重新选中。

      【讨论】:

        【解决方案5】:

        保持CheckBox 状态可以轻松解决问题。一个自定义和解释的完整示例类代码如下_

         public class AreaDataAdapter extends ArrayAdapter<String> {
            private List<String> areaList;
            private Activity context;
            ArrayList<Boolean> positionArray;
        
            public AreaDataAdapter(Context context, int textViewResourceId,
                    List<String> offersAreaList) {
                super(context, textViewResourceId, offersAreaList);
                // TODO Auto-generated constructor stub
                this.context=context;
                this.areaList = offersAreaList;
        
                 positionArray = new ArrayList<Boolean>(offersAreaList.size());
                    for(int i =0;i<offersAreaList.size();i++){
                        positionArray.add(false);
                  }
        
            }
        
            public AreaDataAdapter(Activity context, List<String> offersAreaList) {
                super(ShowOffersActivity.this, R.layout.filterlist_row, offersAreaList);
                this.context=context;
                this.areaList=offersAreaList;
        
                 positionArray = new ArrayList<Boolean>(offersAreaList.size());
                    for(int i =0;i<offersAreaList.size();i++){
                        positionArray.add(false);
                  }
        
            }
        
            public View getView(final int position, View convertView,ViewGroup parent) {
                View row=convertView;
                FilterViewHolder holder;
                if (row==null) {
                    LayoutInflater inflater=getLayoutInflater();
        
                    row=inflater.inflate(R.layout.filterlist_row, parent, false);
                    holder = new FilterViewHolder();
                    holder.filterCheckBox = (CheckBox)row.findViewById(R.id.filter_checkbox);
                    holder.filterCheckBox.setTypeface(fontFace);
        
                    row.setTag(holder);
                } else {
                    //holder = (View) row;
                    holder = (FilterViewHolder) row.getTag();
        
                    /* When a listview recycles views , it recycles its present state as well as listeners attached to it.
                     * if the checkbox was checked and has a onCheckedChangeListener set, both will remain a part of 
                     * recycled view based on position. So it is our responsibility to reset all states and remove
                     *  previous listeners.
                     *  The listener was removed as below:-
                     */
                    holder.filterCheckBox.setOnCheckedChangeListener(null);
        
                }
        
                    holder.filterCheckBox.setText(areaList.get(position));
                    holder.filterCheckBox.setFocusable(false);
                    holder.filterCheckBox.setChecked(positionArray.get(position));
                    holder.filterCheckBox.setText(areaList.get(position));
        
                    holder.filterCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
        
                        @Override
                        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                            // TODO Auto-generated method stub
                            if (isChecked) {
                                //change state of concern item in 'positionArray' array to 'True'
                                 positionArray.set(position, true);
                                //add the checked item in to area list which used to filter offers.
                                filterOffersByThisAreaList.add(areaList.get(position));
        
                            } else {
                               //change state of concern item in 'positionArray' array to 'True'
                                 positionArray.set(position, true);
                                //remove the unchecked item in to area list which used to filter offers.
                                filterOffersByThisAreaList.remove(areaList.get(position));
        
                            }
                        }
                    });
                return row;
            }
        
        }
        

        包含这些CheckBoxs 的ListView 的自定义行(filterlist_row) 如下_

        <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
        
        <CheckBox
            android:id="@+id/filter_checkbox"
            style="@style/CodeFont"
            android:button="@drawable/bg_custom_checkbox"
            android:text="Indian" />
        
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#c9c9c3" />
        
        </LinearLayout>
        

        自定义CheckBox背景如下_

        <?xml version="1.0" encoding="utf-8"?>
        <selector xmlns:android="http://schemas.android.com/apk/res/android">
         <item android:drawable="@drawable/checkbox_s" android:state_checked="true"></item>
         <item android:drawable="@drawable/checkbox_ns" android:state_checked="false"></item>
        
        </selector>
        

        【讨论】:

          【解决方案6】:
          @Override
          public int getViewTypeCount() {
              return getCount();
          }
          
          @Override
          public int getItemViewType(int position) {
              return position;
          }
          
          @Override
          public int getCount() {
              return names.length;
          }
          
          @Override
          public long getItemId(int position) {
              return 0;
          }
          

          在自定义适配器中添加此方法。它对我有用

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-06-30
            • 2018-05-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-06-09
            相关资源
            最近更新 更多