码迷,mamicode.com
首页 > 其他好文 > 详细

【listview精深系列1】listview遇到checkbox碰撞出的火花

时间:2015-05-19 22:42:40      阅读:295      评论:0      收藏:0      [点我收藏+]

标签:

两个问题:

??实现下面的布局很简单,但是这里面有两个问题:

技术分享

问题1: 当listview滑动的时候,怎么保证checkbox选中的状态不乱掉

问题2: 取消和调整按钮的监听方法如何写

取消:就是取消选择的意思。
确定:就是把选中的items的内容取到并保存到数据库中。

问题1解决方案:

??我当时认为出现ListView item中有CheckBox,带来的选择状态混乱,是因为view的复用,但是当我不使用view的复用的时候,依然会出问题。其实归根结底的原因在于一旦item划出屏幕:

  1. 如果view不复用,下一次你再下拉把该item调入屏幕的时候,framework重新调用了一次getiew方法,而这个方法又把布局对象新建了一次(也就是说,如果你不复用旧的布局对象,你也再也用不了旧的布局对象了),而不是按照我们的想法:把旧的布局对象再调出来显示。所以事实上,我们以前的选中状态被清空了。
    !!!
    我们写的布局文件,framework都帮我们解析后建立了对应的对象实例,比如标记linnearlayout,就被系统建立成了linnearlayout对象,比如按钮标记,就被系统建立成了按钮对象。我们checkbox的选中状态,系统也会建立相应的checkbox对象,该对象的check变量为true。

  2. 如果view复用了,必然就造成checkbox的混乱了,比如第10个item复用了第一个布局对象,如果第一个布局对象选中了,那么第10个item,即使你没有选中,你也会惊讶的发现他被选中了~~~~

方法1:复用view,当选择Checkbox的时候,记下其状态,用map保存下来

package com.ht.phoneguard.adapter;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import com.ht.phoneguard.R;
import com.ht.phoneguard.pojo.Info;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by IntelliJ IDEA
 * Project: com.ht.mynote.adapters
 * Author: 安诺爱成长
 * Email: 1399487511@qq.com
 * Date: 2015/5/2
 */
public class ContactsAdapter extends BaseAdapter {
    private Context context;
    private List<Info> list;
    public  Map<Integer,Boolean> mCBFlag = null;

    public ContactsAdapter(Context context, List<Info> list) {
        this.context = context;
        this.list = list;
        mCBFlag = new HashMap<Integer, Boolean>();
        init();
    }

    //初始化CheckBox状态
    void init(){
        for (int i = 0; i < list.size(); i++) {
            mCBFlag.put(i, false);
        }
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int i) {
        return list.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder viewHolder = null;
        if (view == null) {
            view = LayoutInflater.from(context).inflate(R.layout.contacts_item, null);
            viewHolder = new ViewHolder();
            viewHolder.name = (TextView) view.findViewById(R.id.name);
            viewHolder.number = (TextView) view.findViewById(R.id.number);
            viewHolder.check = (CheckBox) view.findViewById(R.id.check);
            view.setTag(viewHolder);
        }
        else
            viewHolder = (ViewHolder) view.getTag();
        viewHolder.name.setText(list.get(i).getName());
        viewHolder.number.setText(list.get(i).getPhonenumber());
        viewHolder.check.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
                if (isChecked) {
                    mCBFlag.put(i, true);
                } else {
                    mCBFlag.put(i, false);
                }
            }
        });
        /*CheckBox监听事件必须放在setChecked之前,否则后果自负*/
        viewHolder.check.setChecked(mCBFlag.get(i));
        Log.d("position:", "i=" + i + ",view=" + view);
        return view;
    }

    private class ViewHolder {
        private TextView name;
        private TextView number;
        private CheckBox check;
    }

    public Map<Integer, Boolean> getmCBFlag() {
        return mCBFlag;
    }

    public void setmCBFlag(Map<Integer, Boolean> mCBFlag) {
        this.mCBFlag = mCBFlag;
    }
}

对此方法更深入的分析:listview与checkbox结合,界面混乱问题:http://my.oschina.net/u/1014842/blog/283285

方法2:在你的实体类中加一个标志位。判断checkbox是否点击了

http://94it.net/a/jingxuanboke/2015/0107/443594.html

问题2错误解决方案:

如何清空checkbox内容

//取消按钮的点击事件
    public void concelOnClick(View view) {
        // 遍历listview的长度,将已选的按钮设为未选
        for (int i = 0; i < listView.getChildCount(); i++) {
            RelativeLayout layout = (RelativeLayout) listView.getChildAt(i);
            CheckBox checkBox = (CheckBox) layout.findViewById(R.id.check);
            if (checkBox.isChecked()) {
                checkBox.setChecked(false);
            }
        }
    }

如何取到checkbox选中这一行的内容

   //确定按钮的点击事件
    public void ensureOnClick(View view) {
        // 遍历listview的长度,找到选中的按钮,然后把其中的的姓名和电话取出来,存到新建的数据库中
        for (int i = 0; i < listView.getChildCount(); i++) {
            RelativeLayout layout = (RelativeLayout) listView.getChildAt(i);
            CheckBox checkBox = (CheckBox) layout.findViewById(R.id.check);
            TextView name = (TextView) layout.findViewById(R.id.name);
            TextView number = (TextView) layout.findViewById(R.id.number);
            if (checkBox.isChecked()) {
                //取出这条数据,然后存放到数据库中
                Info info = new Info();
                info.setName(name.getText().toString());
                info.setPhonenumber(number.getText().toString());
                DbManager.getInstance().addInfo(info);
            }
        }
        this.finish();
    }

错误分析:

??这个方法看上去非常的精妙,简直滴水不漏,事实上他是错误的。因为这样做只是把列表中当前可见的item项选上。清空的也是当前列表可见的item的选中状态。

为什么呢?

??这得追溯到ListView.getCount() 与 ListView.getChildCount()的区别,另外还有ListView中getChildAt(index)该方法的含义。

知识讲解:ListView.getCount() 与 ListView.getChildCount()的区别

??ListView.getCount()(实际上是 AdapterView.getCount()) 返回的是其 Adapter.getCount() 返回的值。也就是“所包含的 Item 总个数”。

??ListView.getChildCount()(ViewGroup.getChildCount) 返回的是显示层面上的“所包含的子 View 个数”。

?? 二者有什么不同?当 ListView 中的 Item 比较少无需滚动即可全部显示时,二者是等价的;当 Item 个数较多需要滚动才能浏览全部的话, getChildCount() < getCount() 其中 getChildCount() 返回的是当前可见的 Item 个数。

??其实 Android framework 的这一设计并不难理解:当一些 Item 当前不显示的时候为什么还要保留它们的 View 呢?移动设备的资源有限,“能省则省”嘛。

05-19 20:03:14.320    5871-5871/com.ht.phoneguard D/position:listview﹕ 9
05-19 20:03:14.320    5871-5871/com.ht.phoneguard D/position:adapter﹕ 62

??以上是测试打印出来的数据,不难发现:
ListView.getCount()是adapter.getCount()的值,不论是否复用,等于数据的总长度.
ListView.getChildCount()不论是否复用,都是一屏的行数.

知识讲解:ListView中getChildAt(index)的使用注意事项

??在很多时候ListView列表数据不需要全部刷新,只需刷新有数据变化的那一条,这时可以用getChildAt(index)获取某个指定position的view,并对该view进行刷新。

??注意:在ListView中,使用getChildAt(index)的取值,只能是当前可见区域(列表可滚动)的子项!

??即取值范围在 >= ListView.getFirstVisiblePosition() && <= ListView.getLastVisiblePosition();

  1. 所以如果想获取前部的将会出现返回Null值空指针问题;
  2. getChildCount跟getCount获取的值将会不一样(数量多时);
  3. 如果使用了getChildAt(index).findViewById(…)设置值的话,滚动列表时值就会改变了。 需要使用getFirstVisiblePosition()获得第一个可见的位置,再用当前的position-它,再用getChildAt取值!即getChildAt(position - ListView。getFirstVisiblePosition()).findViewById(…)去设置值
  4. 如果想更新某一行数据,需要配合ListView的滚动状态使用,一般不滚动时才加载更新数据.
//全局变量,用来记录ScrollView的滚动状态,1表示开始滚动,2表示正在滚动,0表示停止滚动  
伪代码 
ListView设置 
public int scrollStates; 
class OnScrollListenerImpl implements OnScrollListener{ 
@Override 
public void onScrollStateChanged(AbsListView view, int scrollState) { 
scrollStates = scrollState;  
} 

@Override 
public void onScroll(AbsListView view, int firstVisibleItem, 
int visibleItemCount, int totalItemCount) { 
int lastInScreen = firstVisibleItem + visibleItemCount; 
} 
listView.setOnScrollListener(new OnScrollListenerImpl()); 


Activity中 
if(scrollStates==OnScrollListener.SCROLL_STATE_IDLE){ 

更新视图数据 
} 

问题2正确解决方案:

??下列方法的中心思想就是:更新checkbox状态保存集合的内容,然后去更新listview的view。

如何清空checkbox内容

//取消按钮的点击事件
    public void concelOnClick(View view) {
        // 遍历listview的长度,将已选的按钮设为未选
        for (int i = 0; i < infoList.size(); i++) {

            if ((adapter.getmCBFlag()).get(i)) {
                adapter.getmCBFlag().put(i, false);
            }
            adapter.notifyDataSetChanged();
        }
    }

如何取到checkbox选中这一行的内容

 //确定按钮的点击事件
    public void ensureOnClick(View view) {
        // 遍历listview的长度,找到选中的按钮,然后把其中的的姓名和电话取出来,存到新建的数据库中
        for (int i = 0; i < infoList.size(); i++) {
            if (adapter.getmCBFlag().get(i)) {
                //取出这条数据,然后存放到数据库中
                Info info = new Info();
                info.setName(infoList.get(i).getName());
                info.setPhonenumber(infoList.get(i).getPhonenumber());
                DbManager.getInstance().addInfo(info);
            }
        }
        this.finish();
    }

【listview精深系列1】listview遇到checkbox碰撞出的火花

标签:

原文地址:http://blog.csdn.net/a910626/article/details/45850151

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!