带有自定义适配器的列表视图,其中包含复选框

时间:2022-04-10 09:14:26

I have a ListView which uses a custom adapter as shown:

我有一个列表视图,它使用自定义适配器,如下所示:

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. 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.

任何指针表示赞赏。

3 个解决方案

#1


15  

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). An improved getView method which will recycle views is the one below:

答案中的代码可以工作,但效率很低(实际上可以看到,只需滚动ListView并检查Logcat,就可以看到垃圾收集器在工作)。一个改进的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). Maybe you're doing something else that makes the adapter to not work?

关于您的问题中的代码,起初我认为它是错误的,因为您设置行的方式,但是我不明白为什么当您将row视图从列表中分离时,适配器会有这种行为。此外,我还对代码进行了测试,对于复选框(但内存处理非常糟糕),它的工作效果非常好。也许您正在做的其他事情使适配器无法工作?

#2


1  

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. You should be reusing convertView when it is non-null, and creating your view when convertView is null. There are many tutorials around which show you how to do this.

首先,我要说的是,您已经抛弃了使用适配器的一个主要好处:可重用视图。对每个创建的视图持有一个硬引用会有很大的风险影响内存上限。当convertView为非空时,应该重用它,在convertView为空时创建视图。有很多教程向您展示了如何做到这一点。

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. This will supersede any touch listeners on the individual views. Try setting android:clickable="true" on the CheckBox in XML.

在适配器中使用的视图通常有一个OnClickListener被父视图连接到它们上,这样您就可以在ListView上设置OnItemClickListener。这将取代任何单个视图的触摸侦听器。尝试在XML的复选框上设置android:clickable="true"。

#3


0  

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会导致所有东西都出现抖动,复选框无法响应。

The only thing that worked was creating a new View everytime getView() is called.

唯一起作用的是每次调用getView()时创建一个新视图。

    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识别哪个复选框,而不是为每个复选框都有一个新的侦听器。

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.

到目前为止,我还没有把它标记为正确的答案,因为我希望其他人能够对每次重建视图的相当浪费的工作有所贡献。

#1


15  

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). An improved getView method which will recycle views is the one below:

答案中的代码可以工作,但效率很低(实际上可以看到,只需滚动ListView并检查Logcat,就可以看到垃圾收集器在工作)。一个改进的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). Maybe you're doing something else that makes the adapter to not work?

关于您的问题中的代码,起初我认为它是错误的,因为您设置行的方式,但是我不明白为什么当您将row视图从列表中分离时,适配器会有这种行为。此外,我还对代码进行了测试,对于复选框(但内存处理非常糟糕),它的工作效果非常好。也许您正在做的其他事情使适配器无法工作?

#2


1  

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. You should be reusing convertView when it is non-null, and creating your view when convertView is null. There are many tutorials around which show you how to do this.

首先,我要说的是,您已经抛弃了使用适配器的一个主要好处:可重用视图。对每个创建的视图持有一个硬引用会有很大的风险影响内存上限。当convertView为非空时,应该重用它,在convertView为空时创建视图。有很多教程向您展示了如何做到这一点。

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. This will supersede any touch listeners on the individual views. Try setting android:clickable="true" on the CheckBox in XML.

在适配器中使用的视图通常有一个OnClickListener被父视图连接到它们上,这样您就可以在ListView上设置OnItemClickListener。这将取代任何单个视图的触摸侦听器。尝试在XML的复选框上设置android:clickable="true"。

#3


0  

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会导致所有东西都出现抖动,复选框无法响应。

The only thing that worked was creating a new View everytime getView() is called.

唯一起作用的是每次调用getView()时创建一个新视图。

    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识别哪个复选框,而不是为每个复选框都有一个新的侦听器。

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.

到目前为止,我还没有把它标记为正确的答案,因为我希望其他人能够对每次重建视图的相当浪费的工作有所贡献。