自定义带有复选框的ListView,勾选未勾选的项目。

3
我有一个BaseAdapter用于我的ListView,它在fragment中看起来像这样:

public class SelectionMucListAdapter extends BaseAdapter {

    private LayoutInflater inflater = null;
    Typeface titleFace;
    ArrayList<UsersData> innerList = new ArrayList<UsersData>();

    public SelectionMucListAdapter(ArrayList<UsersData> users) {
        inflater = (LayoutInflater) mainActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        titleFace = Typeface.createFromAsset(mainActivity.getAssets(), "fonts/bradybun.ttf");
        innerList = users;
    }

    @Override
    public int getCount() {
        return innerList.size();
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int id) {
        return id;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;

        if (convertView == null) {
            convertView = inflater.inflate(R.layout.muc_list_item, null);
            viewHolder = new ViewHolder();
            viewHolder.image = (ImageView)convertView.findViewById(R.id.item_image);
            viewHolder.check = (CheckBox)convertView.findViewById(R.id.cb_choose_user);
            viewHolder.title = (TextView)convertView.findViewById(R.id.item_text_1);
            viewHolder.rating=(RatingBar)convertView.findViewById(R.id.item_text_2);
            convertView.setTag(viewHolder);

        }else{
            viewHolder = (ViewHolder)convertView.getTag();
        }         

        viewHolder.title.setTypeface(titleFace);

        Bitmap avatar = innerList.get(position).getUserAvatar();//UsersManager.getInstance().getUsers().get(position).getUserAvatar();

        if(avatar != null)
            viewHolder.image.setImageBitmap(avatar);

        viewHolder.title.setText(innerList.get(position).getUserName());
        viewHolder.rating.setRating((float)Double.parseDouble(innerList.get(position).getRating()));
        viewHolder.check.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton cb, boolean isChecked) {
                if(cb.isChecked()) {// if this item is checked then add to list
                    Log.v("CheckBox", "pos: "+position+" checked");
                    if(!choices.contains(innerList.get(position))) {
                        choices.add(innerList.get(position));
                    }
                } else {// if its unchecked then remove from list if exist
                    Log.v("CheckBox", "pos: "+position+" checked");
                    if(choices.contains(innerList.get(position))) {
                        choices.remove(innerList.get(position));
                    }
                }
                isChecked = cb.isChecked();
            }
        });
        viewHolder.check.setTag(position);

        return convertView;
    }
}

static class ViewHolder{
    ImageView image;
    CheckBox check;
    TextView title;
    RatingBar rating;
}

class CheckState{
    boolean isChecked;
    UsersData usersData;

    public UsersData getUsersData() {
        return usersData;
    }

    public void setUsersData(UsersData usersData) {
        this.usersData = usersData;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean isChecked) {
        this.isChecked = isChecked;
    }

}

我有20个列表项,问题是当我选中前两个项目(例如索引[0]和[1])时,当我滚动列表时,其他项目(索引[9]和[10])也自动被选中。如果我选中其他项目,同样的情况也会发生,并且每次我滚动列表时都会以相同的模式重复选中。

实际上我不知道发生了什么,所以我想问问这里是否有人能够解决这个混乱。谢谢!


点击列表视图时打印项目。 - KOTIOS
请检查此链接,它很有帮助.. http://lalit3686.blogspot.in/2012/06/today-i-am-going-to-show-how-to-deal.html - RStar
5个回答

4

您的自定义适配器必须实现 CompoundButton.OnCheckedChangeListener 接口。使用 SparseBooleanArray

然后

 cb.setChecked(mCheckStates.get(position, false)); // cb is checkbox
 cb.setOnCheckedChangeListener(this);

然后使用选中状态来设置复选框的文本。
  public boolean isChecked(int position) {
    return mCheckStates.get(position, false);
}

public void setChecked(int position, boolean isChecked) {
    mCheckStates.put(position, isChecked);

}

public void toggle(int position) {
    setChecked(position, !isChecked(position));
}
@Override
public void onCheckedChanged(CompoundButton buttonView,
    boolean isChecked) {
 mCheckStates.put((Integer) buttonView.getTag(), isChecked);    

}

关于话题 @ 的讨论

https://groups.google.com/forum/?fromgroups#!topic/android-developers/No0LrgJ6q2M

示例:

使用一个 ViewHolder。以下示例未使用 View Holder。

屏幕底部有一个按钮。当您勾选复选框中的项目时,单击底部按钮时会在 toast 中显示已检查行的项目。

使用下面的代码并根据您的要求进行修改。

public class MainActivity extends Activity implements
AdapterView.OnItemClickListener {
    int count;
private CheckBoxAdapter mCheckBoxAdapter;

String[] GENRES = new String[] {
    "Action", "Adventure", "Animation", "Children", "Comedy",
"Documentary", "Drama",
    "Foreign", "History", "Independent", "Romance", "Sci-Fi",
"Television", "Thriller"
};

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ListView listView = (ListView) findViewById(R.id.lv);

    listView.setItemsCanFocus(false);
    listView.setTextFilterEnabled(true);
    listView.setOnItemClickListener(this);
    mCheckBoxAdapter = new CheckBoxAdapter(this, GENRES);
           listView.setAdapter(mCheckBoxAdapter);
    Button b= (Button) findViewById(R.id.button1);
    b.setOnClickListener(new OnClickListener()
    {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            StringBuilder result = new StringBuilder();
            for(int i=0;i<mCheckBoxAdapter.mCheckStates.size();i++)
            {
                if(mCheckBoxAdapter.mCheckStates.get(i)==true)
                {
                    result.append(GENRES[i]);
                    result.append("\n");
                }

            }
            Toast.makeText(MainActivity.this, result, 1000).show();
        }

    });
   }

public void onItemClick(AdapterView parent, View view, int
position, long id) {
    mCheckBoxAdapter.toggle(position);
}

class CheckBoxAdapter extends ArrayAdapter implements CompoundButton.OnCheckedChangeListener
{  private SparseBooleanArray mCheckStates;
   LayoutInflater mInflater;
    TextView tv1,tv;
    CheckBox cb;
    String[] gen;
    CheckBoxAdapter(MainActivity context, String[] genres)
    {
        super(context,0,genres);
        mCheckStates = new SparseBooleanArray(genres.length);
        mInflater = (LayoutInflater)MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        gen= genres;
    }
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return gen.length;
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub

        return 0;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        View vi=convertView;
        if(convertView==null)
         vi = mInflater.inflate(R.layout.checkbox, null); 
         tv= (TextView) vi.findViewById(R.id.textView1);

         cb = (CheckBox) vi.findViewById(R.id.checkBox1);
         tv.setText("Name :"+ gen [position]);
         cb.setTag(position);
         cb.setChecked(mCheckStates.get(position, false));
        cb.setOnCheckedChangeListener(this);
        return vi;
    }
     public boolean isChecked(int position) {
            return mCheckStates.get(position, false);
        }

        public void setChecked(int position, boolean isChecked) {
            mCheckStates.put(position, isChecked);

        }

        public void toggle(int position) {
            setChecked(position, !isChecked(position));

        }
    @Override
    public void onCheckedChanged(CompoundButton buttonView,
            boolean isChecked) {

         mCheckStates.put((Integer) buttonView.getTag(), isChecked);    

    }

}

}

没有必要使用SparseBooleanArray。你只需要在你的数据模型中添加一个布尔字段,该字段由你的适配器使用。 - pskink
@pskink 我从安卓开发者组的Romain Guy的解决方案中选取了它。如果你有其他的解决方案,请贴出来。对于这个已发布的解决方案,你需要使用SparseBooleanArray - Raghunandan
OP的解决方案就像我所说的那样,他需要在他的UsersData数据模型中添加一个布尔字段,在getView方法中进行适当的映射,当然还要在CheckBox监听器中更新此字段。 - pskink

1
我目前正在使用这里提供的解决方案:ListView with CheckBox Scrolling Issue
它对我有效,我已经按照这种方式更改了我的适配器类:
public class SelectionMucListAdapter extends ArrayAdapter<Model> {

    private final FragmentActivity context;
    Typeface titleFace;
    ArrayList<Model> innerList 
        = new ArrayList<Model>();
    boolean checkAll_flag = false;
    boolean checkItem_flag = false;

    public SelectionMucListAdapter(FragmentActivity context
            , ArrayList<Model> users) {
        super(context, R.layout.muc_list_item, users);

        this.context = context;
        titleFace = Typeface.createFromAsset
                (mainActivity.getAssets(), "fonts/bradybun.ttf");
        this.innerList = users;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = null;

        if (convertView == null) {
            LayoutInflater inflator = context.getLayoutInflater();
            convertView = inflator.inflate(R.layout.muc_list_item, null);

            viewHolder = new ViewHolder();
            viewHolder.image = (ImageView) convertView.findViewById(R.id.item_image);
            viewHolder.title = (TextView) convertView.findViewById(R.id.item_text_1);
            viewHolder.title.setTypeface(titleFace);
            viewHolder.rating= (RatingBar) convertView.findViewById(R.id.item_text_2);
            viewHolder.check = (CheckBox) convertView.findViewById(R.id.cb_choose_user);

            viewHolder.check.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    // TODO Auto-generated method stub
                    int getPosition = (Integer) buttonView.getTag();  // Here we get the position that we have set for the checkbox using setTag.
                    innerList.get(getPosition).setSelected(buttonView.isChecked()); // Set the value of checkbox to maintain its state.
                    users.get(innerList.get(getPosition).getTrueIndex()).setSelected(buttonView.isChecked());
                }
            });

            convertView.setTag(viewHolder);
            convertView.setTag(R.id.item_image, viewHolder.image);
            convertView.setTag(R.id.item_text_1, viewHolder.title);
            convertView.setTag(R.id.item_text_2, viewHolder.rating);
            convertView.setTag(R.id.cb_choose_user, viewHolder.check);

       } else {
           viewHolder = (ViewHolder) convertView.getTag();
       }       

        viewHolder.check.setTag(position); // This line is important.

        viewHolder.title.setText(innerList.get(position).getData().getUserName());
        viewHolder.rating.setRating((float)
                Double.parseDouble(innerList.get(position).getData().getRating()));
        viewHolder.check.setChecked(innerList.get(position).isSelected());

        Bitmap avatar = innerList.get(position).getData().getUserAvatar();

        if(avatar != null)
            viewHolder.image.setImageBitmap(avatar);

        return convertView;
    }

    class ViewHolder {
        ImageView image;
        CheckBox check;
        TextView title;
        RatingBar rating;
    }
}

我使用两个ArrayList来保存数据:第一个用于所有数据,第二个用于用户当前搜索的数据。因此,当用户输入关键字进行搜索时,检查状态在第一个和第二个ArrayList之间传递,导致选中的项目始终保持不变,无论它们的位置在哪里。
但是感谢raghunandan,你的答案是我达到更好解决方案的基石!

0

我觉得这对你有用,在我的XML中,我使用了TextView和CheckBox。像这样使用getView()方法并使用Bean类来实现活动和适配器之间的通信。

 import java.util.ArrayList;

 import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
 import android.widget.ImageView;
 import android.widget.TextView;

public class MyAdapter extends BaseAdapter{

Context context;
ArrayList list;
LayoutInflater inflator;
public MyAdapter(Context context,ArrayList list)
{
    this.context=context;
    this.list=list;
    inflator=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
    // TODO Auto-generated method stub
    return list.size();
}

@Override
public Object getItem(int position) {
    // TODO Auto-generated method stub
    return list.get(position);
}

@Override
public long getItemId(int position) {
    // TODO Auto-generated method stub
    return 0;
}

public class ViewHolder
{
    ImageView imageView;
    TextView name;
    TextView mob;
    CheckBox check;
    public ViewHolder(View v)
    {
        imageView=(ImageView) v.findViewById(R.id.imageView1);
        name=(TextView) v.findViewById(R.id.textView1);
        mob=(TextView) v.findViewById(R.id.textView2);
        check=(CheckBox) v.findViewById(R.id.checkBox1);
    }
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    View vi=convertView;
    ViewHolder holder;
    final Contacts contacts=(Contacts) list.get(position);
    if(convertView==null)
    {
        vi=inflator.inflate(R.layout.contacts, null);
        holder=new ViewHolder(vi);
        vi.setTag(holder);
    }
    else
    {   

        holder=(ViewHolder) vi.getTag();
    }

    holder.imageView.setImageBitmap(contacts.getImage());
    holder.name.setText(contacts.getName());
    holder.mob.setText(contacts.getNumber());
    holder.check.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            contacts.setCheck(isChecked);
            notifyDataSetChanged();
        }
    });
    holder.check.setChecked(contacts.isCheck());
    return vi;
}

}


如果有任何关于实现的疑问,请提出。 - Rohit Bandil
最终的Contacts contacts = (Contacts) list.get(position); 是什么意思? - sathya

0
这里的技巧是使用一个布尔数组,其大小为listview项目数。目的是标记哪个项目被选中,哪个没有。当您检查一个项目时,请根据其位置更新其状态。希望这可以帮助您。 :)

0
除了@Raghunandan answer之外,Android在滚动时会重复使用listview中的视图,这意味着当您滚动列表视图时,最后一个离开屏幕的视图将成为从另一侧进入屏幕的下一个视图,因此如果1和2被选中,并且在滚动后9和10将被选中,则可能意味着同时在屏幕上显示8个项目,第九个项目使用了第一个项目的视图,因此它将被选中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接