两个问题:
??实现下面的布局很简单,但是这里面有两个问题:
问题1: 当listview滑动的时候,怎么保证checkbox选中的状态不乱掉
问题2: 取消和调整按钮的监听方法如何写
取消:就是取消选择的意思。
确定:就是把选中的items的内容取到并保存到数据库中。
问题1解决方案:
??我当时认为出现ListView item中有CheckBox,带来的选择状态混乱,是因为view的复用,但是当我不使用view的复用的时候,依然会出问题。其实归根结底的原因在于一旦item划出屏幕:
- 如果view不复用,下一次你再下拉把该item调入屏幕的时候,framework重新调用了一次getiew方法,而这个方法又把布局对象新建了一次(也就是说,如果你不复用旧的布局对象,你也再也用不了旧的布局对象了),而不是按照我们的想法:把旧的布局对象再调出来显示。所以事实上,我们以前的选中状态被清空了。
!!!
我们写的布局文件,framework都帮我们解析后建立了对应的对象实例,比如标记linnearlayout,就被系统建立成了linnearlayout对象,比如按钮标记,就被系统建立成了按钮对象。我们checkbox的选中状态,系统也会建立相应的checkbox对象,该对象的check变量为true。
- 如果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();
- 所以如果想获取前部的将会出现返回Null值空指针问题;
- getChildCount跟getCount获取的值将会不一样(数量多时);
- 如果使用了getChildAt(index).findViewById(…)设置值的话,滚动列表时值就会改变了。 需要使用getFirstVisiblePosition()获得第一个可见的位置,再用当前的position-它,再用getChildAt取值!即getChildAt(position - ListView。getFirstVisiblePosition()).findViewById(…)去设置值
- 如果想更新某一行数据,需要配合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();
}