10

我有一个使用自定义适配器的 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];
    }

}

布局相当简单,所以除非有人认为它们相关,否则我不会发布它们。

问题是某些复选框没有响应。它似乎是第一次显示布局时可见的那些。您必须向下滚动才能按预期工作的任何内容。

任何指针表示赞赏。

4

3 回答 3

16

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:

@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?

于 2012-10-31T15:34:59.000 回答
1

首先让我说你放弃了使用适配器的主要好处之一:可重用视图。对每个创建的对象进行硬引用View具有达到内存上限的高风险。您应该convertView在它为非空时重用,并在convertView为空时创建您的视图。有许多教程向您展示如何做到这一点。

适配器中使用的视图通常OnClickListener由父级附加到它们,View以便您可以OnItemClickListenerListView. 这将取代单个视图上的任何触摸侦听器。尝试android:clickable="true"CheckBoxXML 中设置。

于 2012-09-28T20:41:57.907 回答
0

这可能不是最优雅或最有效的解决方案,但它适用于我的情况。出于某种原因,尝试从视图数组或使用 convertView 重用视图会使每件事都变得摇摆不定,并且 CheckBoxes 无法响应。

唯一有效的方法是在每次调用 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 识别哪个 CheckBox,而不是为每个 CheckBox 设置一个新的侦听器。

到目前为止,我还没有将其标记为正确答案,因为我希望其他人可能对每次都相当浪费地重建视图有一些意见。

于 2012-10-05T07:46:33.240 回答