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

两个问题:

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

问题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: [email protected]
 * 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();
    }
时间: 2024-10-13 12:21:48

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

C#程序员学习Android开发系列之ListView

上篇博客解决了Android客户端通过WebService与服务器端程序进行交互的问题,这篇博客重点关注两个问题,一个是Android应用程序如何与本机文件型数据库SQLite进行交互,另一问题则是如何在ListView中按照我们想要的界面效果进行展示.限于篇幅这篇重点讲ListView,下篇博客重点阐述SQLite. ListView是一个常用的数据显示控件,假设我们要做一个简单的界面,如图所示. 这张图是我直接从Android平板电脑(Android 4.2.2)上面截图下来的,就是一个普通

ListView的item中有button和checkbox,listview的点击事件无效

ListView的item中有button和checkbox,listview的点击事件无效,解决办法: 在item布局文件中的根控件中添加属性设置: android:descendantFocusability="blocksDescendants" 如果只能点击checkbox,而无法触发item的点击事件,那么可以禁掉checkbox的焦点获取,不让它可以点击就可以了 checkbox里面加 android:focusable="false" android:

ListView的View回收引起的checkbox状态改变监听等问题解决方案

我的ListView中每行View包含一个ImageView.TextView.CheckBox.当ListView中有一个或一个一行CheckBox被选中就让ListView上面的Button显示,否则就隐藏.因此,需要对每行View中的CheckBox设置监听.我使用CheckBox中的OnCheckedChangeListener监听器,当CheckBox的状态发生改变的时候就会触发这个监听器.先看下我自定义给ListView的Adapter的getView方法中的一些关键代码: 这是ge

C# WinForm开发系列 - ListBox/ListView/Panel

转自会飞的小猪文章 C# WinForm开发系列 - ListBox/ListView/Panel 在博客园看到了一篇博文,觉得很不错,就转载过来了. 包含自定义绘制的ListBox, 带拖动,图片显示, 内嵌其它控件, 打印等扩展功能的ListView(文章及相关代码搜集自网络,仅供学习参考,版权属于原作者! ). 1.ColorListBox   ColorListBox.zip 2.RadioListBox   RadioListBox.rar 3.扩展CheckedListBox控件  

Android优化系列之ListView优化详解

本文内容:adapter,listview的优化,RecycleBi,n优化情况对比,google大会推荐优化, 实现ListView的过程,Adapter起到了至关重要的作用,不仅仅因为getview()方法.那么,先从Adapter说起~ Adapter: 它在ListView和数据源之间起到桥梁的作用,避免listview和数据源直接接触,而导致因为数据源的复杂性使listview显得臃肿. Adapter,适配器,把复杂的数据源适配给listview,很容易联想到适配器模式.   下面是

Android ListView使用BaseAdapter与ListView的优化 (转至 http://www.open-open.com/lib/view/open1339485728006.html)

在ListView的使用中,有时候还需要在里面加入按钮等控件,实现单独的操作.也就是说,这个ListView不再只是展示数据,也不仅仅是这一行要来处理用户的操作,而是里面的控件要获得用户的焦点.读者可以试试用SimpleAdapter添加一个按钮到ListView的条目中,会发现可以添加,但是却无法获得焦点,点击操作被ListView的Item所覆盖.这时候最方便的方法就是使用灵活的适配器BaseAdapter了. ▲图4-35 BaseAdapter中的方法 使用BaseAdapter必须写一

有关Listview分页以及判断Listview是否已经滚动到低端的一些探索

在体验其他设计优美的app时加载动画的假象让我以为Listview的最后一条item完全显示之后才正式加载,导致我走入了一个误区浪费了很长的一段时间,最终我也是妥协以最后一条item刚被暴露开始作为Listview滚动到底部的标志,也就是说无须费很大劲去实现一个并没有多大提升的细节了.期间当然发现了一些很好的解决办法, 其一(网上引用的代码,原始出处不详,故不注明了): 1 private int getLastVisiblePosition = 0, lastVisiblePositionY

Android ListView使用BaseAdapter与ListView的优化

在ListView的使用中,有时候还需要在里面加入按钮等控件,实现单独的操作.也就是说,这个ListView不再只是展示数据,也不仅仅是这一行要来处理用户的操作,而是里面的控件要获得用户的焦点.读者可以试试用SimpleAdapter添加一个按钮到ListView的条目中,会发现可以添加,但是却无法获得焦点,点击操作被ListView的Item所覆盖.这时候最方便的方法就是使用灵活的适配器BaseAdapter了. ▲图4-35 BaseAdapter中的方法 使用BaseAdapter必须写一

【Fragment精深系列5】fragment findViewById()返回null完全解析

一.引入 ??你是不是经常遇到在fragment中调用findViewById方法寻找fragment布局文件中的控件返回null的现象.我之前也遇到了这个问题,虽然后来解决了,但是心中一直有疑惑,最近有时间停下来,结合别人的解答和自己的思考,对这个问题进行彻底的梳理. 二.使用getActivity().findViewById 1.getActivity的介绍 ??Fragment中有一个getActivity()的方法.返回与Fragment关联的Activity对象(通过该对象可以查找a