Android ListView的一个坑,你可掉进去过?

  需要的功能很简单,就是一个带checkbox的列表,提交时需要知道用户选择了那些项目,如下图:
Android ListView的一个坑,你可掉进去过?

使用SimpleAdapter作为数据适配器,重写SimpleAdapter.ViewBinder的方法,这样用比自定义Adapter要方便点,代码如下
datas定义是private List<Map<String, Object>> datas=null;
其中让Map中保存一项自我引用(my)绑定到checkBox

Android ListView的一个坑,你可掉进去过?
private Map<String, Object> populateMap(String lblNo,

Map<String, Object>... maps) {



Map<String, Object> map = null;

if (maps.length > 0) {

map = maps[0];

} else {

map = new HashMap<String, Object>();

}



map.put("lblNo", lblNo);

map.put("my", map);

map.put("checked", true);

return map;

}
View Code

 

    private void bindAdapter(){

        

        int[] to = new int[] { R.id.lblNo,R.id.ckbIt };

        String[] from = new String[] { "lblNo","my"};

        adapter = new SimpleAdapter(this, datas, R.layout.activity_post_list_item,

                from, to);

        // =======添加删除事件=======

        SimpleAdapter.ViewBinder binder = new SimpleAdapter.ViewBinder() {



            private boolean supressEvent=false;

                    

            @Override

            public boolean setViewValue(View view, Object data,

                    String textRepresentation) {

                final Object d =  data;

                

                if (view instanceof CheckBox) {

            

                     final Map map=(Map)d;

                     CheckBox ckb = (CheckBox) view;

                     Log.d("T", "->Map"+map);

                     //ckb.setTag(map);

                     supressEvent=true; //需要避免在这里触发OnCheckedChange事件监听处理

                     ckb.setChecked((Boolean)map.get("checked"));

                    supressEvent=false;

                     

                     ckb.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                        

                        @Override

                        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                    

                            Log.d("T", "onCheckedChanged->Map"+map +" ischecked:" +isChecked);

                            if(supressEvent)return;

                            //Map map=(Map)buttonView.getTag();

                            map.put("checked", isChecked);

                            

                            

                

                            

                        }

                    });

                    



                    return true; //返回true表示不需要父类进行默认设置

                    //参考http://www.cnblogs.com/carmanloneliness/p/3500832.html

                    //http://www.cnblogs.com/carmanloneliness/p/3500832.html

                }



                return false;

            }

        };

        adapter.setViewBinder(binder);



        // =======End=======



        lv.setAdapter(adapter);

    }

 刚开始时,没加上面红色注释那一句,发现运行时程序行为总不合要求。
后来发现是ckb在执行setChecked时会触发OnCheckedChange处理程序,
而SimpleAdapter采用的也是控件重用机制,就是当列表往上下拖时,那些被拖出屏幕外的控件会重用(绑定新的数据,参考代码里给的那链接),由于使用了final在执行ckb.setChecked((Boolean)map.get("checked")); 触发该控件的OnCheckedChange处理程序,而这个处理程序指向的数据项是前一次绑定的那行即前一次调用setViewValue传入的数据,这样就可能导致datas中的某个数据被意外修改,进而引起程序行为的不确定。

解决办法就是在执行ckb.SetChecked时做个标记,而事件处理程序根据这个标记排除拖动列表产生消息。

你可能感兴趣的:(Android ListView的一个坑,你可掉进去过?)