简体   繁体   English

包含CheckBoxes的自定义适配器的Listview

[英]Listview with custom adapter containing CheckBoxes

I have a ListView which uses a custom adapter as shown: 我有一个ListView,它使用自定义适配器,如下所示:

private class CBAdapter extends BaseAdapter implements OnCheckedChangeListener{

    Context context;
    public String[] englishNames;
    LayoutInflater inflater;
    CheckBox[] checkBoxArray;
    LinearLayout[] viewArray;
    private boolean[] checked;

    public CBAdapter(Context con, String[] engNames){
        context=con;
        englishNames=engNames;
        inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        checked= new boolean[englishNames.length];
        for(int i=0; i<checked.length; i++){
            checked[i]=false;
            //Toast.makeText(con, checked.toString(),Toast.LENGTH_SHORT).show();
        }
        checkBoxArray = new CheckBox[checked.length];
        viewArray = new LinearLayout[checked.length];
    }

    public int getCount() {
        return englishNames.length;
    }

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

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

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

        if(viewArray[position] == null){

            viewArray[position]=(LinearLayout)inflater.inflate(R.layout.record_view_start,null);

            TextView tv=(TextView)viewArray[position].findViewById(R.id.engName);
            tv.setText(englishNames[position]);

            checkBoxArray[position]=(CheckBox)viewArray[position].findViewById(R.id.checkBox1);
        }

        checkBoxArray[position].setChecked(checked[position]);
        checkBoxArray[position].setOnCheckedChangeListener(this);
        return viewArray[position];
    }


    public void checkAll(boolean areChecked){
        for(int i=0; i<checked.length; i++){
            checked[i]=areChecked;
            if(checkBoxArray[i] != null)
                checkBoxArray[i].setChecked(areChecked);
        }
        notifyDataSetChanged();
    }

    public void onCheckedChanged(CompoundButton cb, boolean isChecked) {
        for(int i=0; i<checked.length; i++){
            if(cb == checkBoxArray[i])
                checked[i]=isChecked;
        }




    }
    public boolean itemIsChecked(int i){
        return checked[i];
    }

}

The layouts are fairly simple so I won't post them unless anyone thinks they are relevant. 布局相当简单,所以我不会发布它们,除非有人认为它们是相关的。

The problem is that some of the CheckBoxes are not responding. 问题是一些CheckBox没有响应。 It seems to be the ones that are visible when the layout is first displayed. 它似乎是首次显示布局时可见的。 Any that you have to scroll down to work as expected. 任何你必须向下滚动按预期工作。

Any pointers appreciated. 任何指针赞赏。

Your code from the answer works but is inefficient(you can actually see this, just scroll the ListView and check the Logcat to see the garbage collector doing it's work). 您的答案中的代码有效,但效率低下(您实际上可以看到这一点,只需滚动ListView并检查Logcat以查看垃圾收集器正在执行此操作)。 An improved getView method which will recycle views is the one below: 一个改进的getView方法将回收视图如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
     LinearLayout view = (LinearLayout) convertView;
     if (view == null) {
          view = (LinearLayout) inflater.inflate(R.layout.record_view_start, parent, false);
     }
     TextView tv = (TextView) view.findViewById(R.id.engName);
     tv.setText(getItem(position));
     CheckBox cBox = (CheckBox) view.findViewById(R.id.checkBox1);
     cBox.setTag(Integer.valueOf(position)); // set the tag so we can identify the correct row in the listener
     cBox.setChecked(mChecked[position]); // set the status as we stored it        
     cBox.setOnCheckedChangeListener(mListener); // set the listener    
     return view;
}

OnCheckedChangeListener mListener = new OnCheckedChangeListener() {

     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {   
         mChecked[(Integer)buttonView.getTag()] = isChecked; // get the tag so we know the row and store the status 
     }
};

Regarding your code from your question, at first I thought it was wrong because of the way you setup the rows but I don't see why the adapter will have that behavior as you detached the row view from the list. 关于你的问题的代码,起初我认为这是错误的,因为你设置行的方式,但我不明白为什么适配器将具有该行为,因为你从列表中分离行视图。 Also, I even tested the code and it works quite well regarding CheckBoxes (but with very poor memory handling). 此外,我甚至测试了代码,它在CheckBoxes工作得很好(但内存处理非常糟糕)。 Maybe you're doing something else that makes the adapter to not work? 也许你正在做其他让适配器无法工作的东西?

Let me first say that you have thrown away one of the main benefits of using an adapter: Reusable views. 首先让我说你已经抛弃了使用适配器的一个主要好处:可重用的视图。 Holding a hard reference to each created View holds a high risk of hitting the memory ceiling. 对每个创建的View持有一个硬引用会占用内存上限的高风险。 You should be reusing convertView when it is non-null, and creating your view when convertView is null. convertView为非null时,您应该重用convertView ,并在convertView为null时创建视图。 There are many tutorials around which show you how to do this. 有很多教程可以告诉你如何做到这一点。

Views used in an adapter typically have an OnClickListener attached to them by the parent View so that you can set a OnItemClickListener on the ListView . 适配器中使用的视图通常具有由父View附加到它们的OnClickListener ,以便您可以在ListView上设置OnItemClickListener This will supersede any touch listeners on the individual views. 这将取代个人观点上的任何触摸听众。 Try setting android:clickable="true" on the CheckBox in XML. 尝试在XML中的CheckBox上设置android:clickable="true"

This may not be the most elegant or efficient solution but it works for my situation. 这可能不是最优雅或最有效的解决方案,但它适用于我的情况。 For some reason attempting to reuse the views either from an array of views or using convertView makes every thing go wobbley and the CheckBoxes fail to respond. 出于某种原因,尝试从视图数组或使用convertView重用视图会使每个事情变得不稳定并且CheckBoxes无法响应。

The only thing that worked was creating a new View everytime getView() is called. 唯一有效的方法是每次调用getView()时创建一个新的View。

    public View getView(final int position, View convertView, ViewGroup parent) {
        LinearLayout view;
        view=(LinearLayout)inflater.inflate(R.layout.record_view_start,null);


        TextView tv=(TextView)view.findViewById(R.id.engName);
        tv.setText(englishNames[position]);

        CheckBox cBox=(CheckBox)view.findViewById(R.id.checkBox1);
        cBox.setChecked(checked[position]);
        cBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){

            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                checked[position]=isChecked;
            }
        });
        return view;
    }

Finding this solution was also hampered by the fact that I was calling a separately defined onCheckedChangedListener, that then identified which CheckBox by id, rather than having a new listener for each CheckBox. 由于我正在调用一个单独定义的onCheckedChangedListener,然后通过id识别哪个CheckBox,而不是为每个CheckBox创建一个新的侦听器,因此查找此解决方案也受到了阻碍。

As yet I haven't marked this as the correct answer as I'm hoping that others may have some input regarding the rather wasteful rebuilding the view every time. 到目前为止,我还没有将此标记为正确的答案,因为我希望其他人可能会对每次重建视图相当浪费。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM