Java 带有包含复选框的自定义适配器的Listview
我有一个使用自定义适配器的ListView,如图所示:Java 带有包含复选框的自定义适配器的Listview,java,android,listview,checkbox,adapter,Java,Android,Listview,Checkbox,Adapter,我有一个使用自定义适配器的ListView,如图所示: private class CBAdapter extends BaseAdapter implements OnCheckedChangeListener{ Context context; public String[] englishNames; LayoutInflater inflater; CheckBox[] checkBoxArray; LinearLayout[] viewArra
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];
}
}
私有类CBAdapter扩展BaseAdapter实现OnCheckedChangeListener{
语境;
公共字符串[]英文名称;
充气机;
复选框[]复选框数组;
线性布局[]视图阵列;
检查私有布尔[];
公共CBAdapter(上下文con,字符串[]engNames){
上下文=con;
英语名称=英语名称;
充气器=(LayoutFlater)context.getSystemService(context.LAYOUT\u充气器\u服务);
选中=新布尔值[englishNames.length];
对于(int i=0;i首先让我说,您已经放弃了使用适配器的一个主要好处:可重用视图。对每个创建的视图
进行硬引用具有很高的内存上限风险。您应该在convertView
非空时重用它,并在convertView
i时创建视图有很多教程向你展示了如何做到这一点
适配器中使用的视图通常由父视图视图附加一个OnClickListener
,这样您就可以在列表视图上设置一个OnItemClickListener
。这将取代单个视图上的任何触摸式侦听器。请尝试设置android:clickable=“true”
在XML中的复选框上。这可能不是最优雅或最有效的解决方案,但它适用于我的情况。出于某种原因,尝试重用视图数组中的视图或使用convertView会使每件事都不稳定,复选框无法响应
唯一有效的方法是每次调用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;
}
我调用了一个单独定义的onCheckedChangedListener,然后通过id标识出哪个复选框,而不是为每个复选框指定一个新的侦听器,这一事实也阻碍了找到这个解决方案
到目前为止,我还没有将此标记为正确答案,因为我希望其他人可能会有一些关于每次重建视图时相当浪费的信息。答案中的代码工作正常,但效率低下(实际上,您可以看到这一点,只需滚动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
}
};
关于您的问题中的代码,起初我认为这是错误的,因为您设置行的方式不同,但我不明白当您从列表中分离行视图时,适配器为什么会有这种行为。此外,我甚至测试了代码,它在复选框方面运行得非常好(但内存处理非常差)。可能您正在做一些其他事情,使适配器无法工作?感谢您对convertView的提醒。我一直在想这个参数的作用。我想我可能有一个解决原始问题的方法,但我仍在测试中。我很快会再次发布。对于使用的ListView
,在堆栈溢出方面有很多问题一个使用带有复选框的行的自定义适配器,您应该更多地了解这些解决方案。您的代码可以工作,但效率非常低,每次滚动列表视图时,您都会创建新的视图。我完全同意Luksprog,但在多次尝试之后,使用适用于其他人的解决方案,我找不到一个有效的解决方案ks除此之外。希望垃圾收集能跟上浪费的视图,不会造成内存问题,旧手机也不会太慢。在我的应用程序中,在大多数情况下,listview不会有太多条目,所以滚动应该保持在最小值。你的代码现在工作的原因是因为你每次都会膨胀行您可以避免OnCheckedChangeListener
搞乱复选框的状态。您永远不应该忽略getView
方法中的视图回收。这是您改进的getView
方法,它可以做您想要的事情,但也可以回收视图。非常好。这完全符合预期。您的代码删除了strai只从“checked”数组中删除“m”(这是我在java中的第一个项目,我不知道这些约定)我现在有两个问题要问。1.你能把它作为一个答案转发给我吗,这样我就可以把它设置为被接受的;2.你能解释一下为什么我的代码在最初的帖子中没有这样做。我感觉这个答案可能会让我了解一些我正在努力解决的其他概念。对于带有子复选框的EcxpandableListView,你是如何做到这一点的s?@RicNjesh您需要使用getChildView()
方法,该方法在需要子行时调用。在这种情况下需要检查什么以及如何声明它?@MuneemHabib它是一个布尔数组,与列表视图中的项数一样大。
。在适配器的构造函数中声明它。@Luksprog谢谢,先生,它帮助了我很多。我已经解决了我的问题