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


      需要的功能很简单,就是一个带checkbox的列表,提交时需要知道用户选择了那些项目,如下图:

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

    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时做个标记,而事件处理程序根据这个标记排除拖动列表产生消息。

  • 相关阅读:
    angularjs中的页面访问权限设置
    Html页面head标签元素的意义和应用场景
    脚本引用中的defer和async的用法和区别
    自适应页面中如何使用雪碧图
    网页颜色分辨测试小游戏的js化辨别及优化
    jQuery1.9及其以上版本中动态元素on绑定事件无效解决方案
    Data URL简介及Data URL的利弊
    浏览器调试:事件定位与源码查找
    简述ES5 ES6
    移动端中pagehide、pageshow的应用
  • 原文地址:https://www.cnblogs.com/wdfrog/p/3678658.html
Copyright © 2020-2023  润新知