`
treagzhao
  • 浏览: 6573 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
社区版块
存档分类
最新评论

Android开发中ListView多屏的全选、反选功能

阅读更多
     鄙人最近刚开始学习Android,在练习的时候写到一个ListView的全选反选功能。本来以为这个功能很简单,随随便便就能搞定,结果真的下手去做的时候被虐的死去活来,不知道为什么在做全选和反选的操作是,总有几个选项在意料之外被选上或者取消,导致每次反选的结果都不是我预期的结果。而且在全选或者反选之后再去点checkbox,会出现操作无效的情况,一滚动屏幕,选项就又回到全选的状态了。

    后来问了一些以前开发过Android的人才知道,原来Android里面的ListView是基于这样一种工作原理:当数据超过一屏的时候,每次滚动都会重新加载ListView里面每一行的View。而新加进来的View实际上是之前的View重新加载。这样ListView就只需要加载一个屏幕所展示数量的View就可以了,但是副作用就是消失到屏幕外的View系统不会为你保存。举个例子,一屏能显示7行数据,要显示的数据总共有10行。那头一屏必然是0~6,当滚到第二屏的时候,就是6、7、8、9、0、1。头几个被滚出屏幕的View又重新被加载了回来。我之前用的方法是通过for循环获取ListView里面的子元素然后改变CheckBox的状态,由于刚才那个原因,总是存在序号错位的问题,所以才会总是出现刚才说的全选和反选时的诡异现象。

     所以解决办法除了要把当前屏幕内的checkBox的状态按照原来数据的序号进行改变以外,还要同时修改adapter里面的数据,以确保滚屏的时候数据一致。我在看书的时候基本上所有的例子都是把adapter写成一个匿名内部类,这样adapter的数据一定与activity里面的数据是同步的。但是如果adapter里面要实现很多功能的话,就会使activity这个类过于庞大,违反了“指责单一化原则”。而如果把Adapter单独写出来,数据同步就不太好处理。

    所以我抽离了一个中间的专门管理数据的类InAccountListManager(InAccount是我这个工程的某个bean,可以忽略不计),来同步activity与adapter之间数据的差异。
package com.accountms.activity.util;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import android.app.Activity;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ListView;

import com.accountms.activity.R;
import com.accountms.databean.BaseBean;
import com.accountms.databean.InAccountBean;
import com.accountms.functioninterface.IListHandler;
import com.example.util.IBaseInfo;

/**
 * ListView数据管理类
 * 
 * @author Treagzhao 用来同步Activity与Adapter之间的数据,同时管理正向同步和反向同步的互斥。
 * 
 */
public class InAccountListManager implements IBaseInfo, IListAdapter,
		IListActivity {
	private IListActivity activity;
	private IListAdapter adapter;
	// list用来保存要展示的数据(InAccountBean是我要操作的bean类,可以换成任意数据)
	private List<InAccountBean> list;
	// selectedList 用来保存已经选中的数据
	private Set<InAccountBean> selectedList = new HashSet();
	// posList与selectedList数据一致,用以记录所保存的数据在源数据中的位置
	private Set<Integer> posList = new HashSet();
	// 如果正在执行“全选”或者“反选”操作时,用于取消checkbox的onCheckedChagne事件(下面会有详细说明)
	private boolean operatingMutex = false;
	private ListView listView;

	/**
	 * 
	 * @param list
	 *            源数据
	 * @param activity
	 *            具体的activity
	 * @param adapter
	 *            listView的adapter
	 * @param listView
	 *            展示数据的listView
	 */
	public InAccountListManager(List<InAccountBean> list,
			IListActivity activity, IListAdapter adapter, ListView listView) {
		this.activity = activity;
		this.adapter = adapter;
		this.list = list;
		this.listView = listView;
	}

	public Set<InAccountBean> getSelectedList() {
		return selectedList;
	}

	public void setSelectedList(Set<InAccountBean> selectedList) {
		this.selectedList = selectedList;
	}

	public Set<Integer> getPosList() {
		return posList;
	}

	public void setPosList(Set<Integer> posList) {
		this.posList = posList;
	}

	/**
	 * 继承IListAdapter接口的方法,用以处理用户点击“全选”后的操作
	 */
	@Override
	public void onSelectedAll() {
		// “全选”
		// 是一个activity向adapter单向同步数据的操作,可是在执行checkbox.setChecked时,会触发checkbox的onCheckedChange事件,会把adapter的数据重新发回来,造成干扰。所以要设定一个标记量,activity向adapter单向同步的时候,不接受反向的同步。
		operatingMutex = true;
		// 将所有数据都放在已选数据中
		for (int i = 0; i < list.size(); i++) {
			InAccountBean bean = list.get(i);
			selectedList.add(bean);
			posList.add(i);
		}
		// 改变checkbox的状态
		for (int i = 0; i < listView.getChildCount(); i++) {
			LinearLayout linear = (LinearLayout) listView.getChildAt(i);
			CheckBox checkbox = (CheckBox) linear
					.findViewById(R.id.inaccount_list_checkbox);
			checkbox.setChecked(true);
		}
		this.adapter.onSelectedAll();
		operatingMutex = false;
	}

	@Override
	public void onSelectedAllCancle() {
		// TODO Auto-generated method stub

	}

	/**
	 * 继承自IListActivity的方法,用以处理checkbox的状态点击事件
	 */
	@Override
	public void onItemCheckedChanged(int pos, boolean checked) {
		//如果是“全选”或者“反选”类的操作则退出,只执行用户点击checkbox这一类的adapter到activity的数据同步
		if (operatingMutex)
			return;
		if (checked) {
			selectedList.add(list.get(pos));
			posList.add(pos);
		} else {
			selectedList.remove(list.get(pos));
			posList.remove(pos);
		}
		this.activity.onItemCheckedChanged(pos, checked);
	}

	/**
	 * 继承自IListAdapter的方法,用以处理用户点击“反选”的操作
	 */
	@Override
	public void onReverseSelected() {
		//原理同上
		operatingMutex = true;
		//将选中的数据逆置
		for (int i = 0; i < list.size(); i++) {
			InAccountBean bean = list.get(i);
			if (selectedList.contains(bean)) {
				selectedList.remove(bean);
				posList.remove(i);
			} else {
				selectedList.add(bean);
				posList.add(i);
			}
		}
		//设定checkbox的状态
		for (int i = 0; i < listView.getChildCount(); i++) {
			LinearLayout linear = (LinearLayout) listView.getChildAt(i);
			CheckBox checkbox = (CheckBox) linear
					.findViewById(R.id.inaccount_list_checkbox);
			//注意这里获取的是checkbox的tag,不是最外层的linear的tag,具体的原理在adapter类里面会提到
			int pos = (Integer) checkbox.getTag();
			if (posList.contains(pos))
				checkbox.setChecked(true);
			else
				checkbox.setChecked(false);
		}
		operatingMutex = false;
	}
}



    这里面用到两个接口IListActivity和IListAdapter,前者的作用是接受adapter发出的onCheckedChange事件,后者是响应“全选”和“反选”。由于这个Manager类相当于activity和adapter之间的中间件,它的作用是把activity的操作指令预处理后传递给adapter,反之亦然,所以Manager类同时实现这两个接口。
package com.accountms.activity.util;

public interface IListActivity {
	public void onItemCheckedChanged(int pos, boolean checked);
}
package com.accountms.activity.util;

public interface IListAdapter {
	public void onSelectedAll();

	public void onSelectedAllCancle();

	public void onReverseSelected();
}


    以下是Activity的代码
package com.accountms.activity;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.accountms.activity.util.IListActivity;
import com.accountms.activity.util.InAccountListManager;
import com.accountms.adapter.InAccountListAdapter;
import com.accountms.databean.InAccountBean;
import com.accountms.functioninterface.IThreadHandler;
import com.accountms.thread.InAccountThreadFactory;
import com.example.util.BaseActivity;
import com.example.util.IBaseInfo;
import android.os.Bundle;
import android.app.ProgressDialog;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class InAccountListActivity extends BaseActivity implements IBaseInfo,
		IThreadHandler, IListActivity {
	
	private TextView selectAll, reverseSelect, removeBtn;
	private ListView listView;
	private Button addBtn;
	private int page = 1;
	private ProgressDialog dialog;
	//用以保存从数据库中读出的信息
	private List<InAccountBean> list;
	//“哒啦啦!”主角出现了
	private InAccountListManager manager;
	//listView 的adapter
	private InAccountListAdapter adapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_in_account_list);

		init();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.in_account_list, menu);
		return true;
	}

	/**
	 * 初始化View
	 */
	@Override
	protected void findView() {
		listView = (ListView) findViewById(R.id.info_list);
		selectAll = (TextView) findViewById(R.id.text_selecteAll);
		reverseSelect = (TextView) findViewById(R.id.text_reverseSelect);
		removeBtn = (TextView) findViewById(R.id.text_remove);
	}

	/**
	 * 绑定事件
	 */
	@Override
	protected void bindListener() {
		selectAll.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View view) {
				if (manager == null)
					return;
				manager.onSelectedAll();
			}
		});
		reverseSelect.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View view) {
				manager.onReverseSelected();
			}
		});
	}

	/**
	 * 初始化各个功能
	 */
	@Override
	protected void initPlugins() {
		initList();
	}

	/**
	 * 初始化数据List,新建一个线程从数据库中获取数据
	 */
	private void initList() {
		dialog = new ProgressDialog(this);
		dialog.setTitle("提醒");
		dialog.setProgress(0);
		dialog.setMax(100);
		dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		dialog.setMessage("读取中····");
		dialog.setCancelable(false);
		dialog.setIndeterminate(false);
		dialog.show();
		Map map = new HashMap();
		map.put("page", page);
		Thread thread = new Thread(InAccountThreadFactory.getThread(this,
				InAccountThreadFactory.TYPE_LIST, map, this, dialog));
		thread.start();
	}

	/**
	 * 获取数据库的线程回调函数,本文所说的内容基本就是从这里开始的
	 */
	@Override
	public void handle(Map map) {
		list = (List<InAccountBean>) map.get("list");
		adapter = new InAccountListAdapter(InAccountListActivity.this, list);
		manager = new InAccountListManager(list, this, adapter, listView);
		adapter.setManager(manager);
		listView.setAdapter(adapter);
		if (dialog != null)
			dialog.cancel();
	}

	@Override
	public void onItemCheckedChanged(int pos, boolean checked) {
		// TODO 做爱做的事
	}

}



    接下来是adapter的代码
package com.accountms.adapter;

import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Set;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.accountms.activity.R;
import com.accountms.activity.util.IListAdapter;
import com.accountms.activity.util.InAccountListManager;
import com.accountms.databean.InAccountBean;
import com.example.util.IBaseInfo;

/**
 * 
 * @author Treagzhao 设定ListView的Adapter
 * 
 */
public class InAccountListAdapter extends BaseAdapter implements IBaseInfo,
		IListAdapter {
	private List<InAccountBean> list;
	public Activity activity;
	private SimpleDateFormat format;
	// 我们的主角在这里
	private InAccountListManager manager;

	public InAccountListManager getManager() {
		return manager;
	}

	public void setManager(InAccountListManager manager) {
		this.manager = manager;
	}

	public InAccountListAdapter(Activity activity, List<InAccountBean> list) {
		this.list = list;
		this.activity = activity;
		format = new SimpleDateFormat("yyyy-MM-dd");
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return list.size();
	}

	@Override
	public Object getItem(int pos) {
		// TODO Auto-generated method stub
		return pos;
	}

	@Override
	public long getItemId(int pos) {
		// TODO Auto-generated method stub
		return pos;
	}

	/**
	 * 看这里看这里看这里
	 * 
	 * @deprecated 这个函数两个情况下会被调用,除了一开始的初始化以外,每次listView滚动时也会调用这个函数。
	 */
	@Override
	public View getView(int pos, View convertView, ViewGroup arg2) {
		LayoutInflater inflater = this.activity.getLayoutInflater();
		LinearLayout linear = null;

		if (convertView == null) {
			linear = (LinearLayout) inflater.inflate(
					R.layout.inaccountlist_layout, null);
			linear.setTag(pos);
		} else {
			linear = (LinearLayout) convertView;
		}
		// 注意这个地方,除了LinearLayout的初始化以外,其内部的所有子View在所有情况下均被执行。
		CheckBox checkbox = (CheckBox) linear
				.findViewById(R.id.inaccount_list_checkbox);
		// 由于checkbox的tag是每次随着list的顺序设定的,所以checkbox的tag一定是与数据一致的
		checkbox.setTag(pos);
		TextView mark = (TextView) linear
				.findViewById(R.id.text_inaccount_mark);
		TextView time = (TextView) linear
				.findViewById(R.id.text_inaccount_time);
		mark.setText(list.get(pos).getMark());
		time.setText(format.format(list.get(pos).getTime()));
		// 获取已经被选中的数据,由于这个数据是每次加载时从manager对象获取,所以一定是同步的数据
		Set<Integer> posList = manager.getPosList();
		if (posList.contains(pos)) {
			checkbox.setChecked(true);
		} else {
			checkbox.setChecked(false);
		}
		checkbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			@Override
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
				int pos = (Integer) buttonView.getTag();
				// 触发manager的onItemCheckedChanged事件
				manager.onItemCheckedChanged(pos, isChecked);
			}
		});
		return linear;
	}

	@Override
	public void onSelectedAll() {
	}

	@Override
	public void onSelectedAllCancle() {
	}

	@Override
	public void onReverseSelected() {
	}

}


    由于activity和adapter的操作都是对manager的操作,而且获取数据是都是从manager里面获取,所以双方的数据一定是一致的。而且有manager在中间进行管理,就会让activity 的操作不会返回来对数据造成影响。同时因为有manager 的存在,大大减少了activity和adapter的工作量。解决了ListView在超过一屏是的全选和反选功能。
    鄙人刚刚开始学习Android开发,如果各位看官有更好的解决方案,请一定告知我
分享到:
评论

相关推荐

    listView,Gridview全选反选关联及删除操作

    在Android开发中,ListView和GridView是两种常用的布局控件,用于展示数据列表。它们都具有良好的可滚动性,适用于展示大量信息。本篇文章将详细探讨如何实现ListView和GridView中的全选、反选以及关联的删除操作,...

    android checkbox全选反选

    "Android CheckBox全选反选"这个话题主要涉及如何在ListView中实现CheckBox的选择与反选择操作,包括全选和反选的功能。ListView是Android系统提供的一种列表视图,它可以显示大量数据并允许用户进行交互。下面我们...

    android ListView中的checkBox全选和反选Demo

    综上所述,这个“android ListView中的checkBox全选和反选Demo”主要展示了如何在ListView中集成CheckBox,实现列表项的全选和反选功能,以及如何优化Adapter以提高性能。理解并掌握这些知识点对于Android开发者来说...

    安卓listview相关相关-ListView的全选反选.rar

    在Android开发中,ListView是常用的一种控件,用于展示大量数据列表。本资源"安卓listview相关相关-ListView的全选反选.rar"主要聚焦于ListView的全选和反选功能,这对于构建具有多选操作的列表应用至关重要。在本文...

    Android ListView 带 CheckBox(全选,反选,全不选)

    本篇文章将深入讲解如何在Android中实现ListView带有CheckBox的功能,包括点击选择、反选、全选和全不选,以及实时显示已选择数量。 1. **ListView基本使用** - ListView是Android中用于显示一列可滚动项目的视图...

    Android 全选反选对话框

    在Android开发中,全选和反选对话框是一种常见的用户交互元素,主要用于处理多选列表或数据集合。这种对话框通常包含一个复选框,允许用户一次性选择或取消选择所有项目,提高操作效率。本篇文章将深入探讨如何在...

    ListView的全选反选

    在许多应用中,我们可能需要实现ListView的全选和反选功能,比如在邮件应用中选择多个邮件进行批量操作。本教程将深入讲解如何在Android Studio(AS)环境下,使用ListView结合CheckBox实现全选和反选的效果。 首先...

    android listview的全选,反选,取消全选(直接拿来用)

    以上就是关于在Android中实现ListView的全选、反选和取消全选功能的详细解释。实际开发时,可以根据项目需求进行适当的调整和优化,例如添加动画效果、实现多选模式等。通过掌握这些知识,可以提升用户在使用应用时...

    ListView多选,全选,反选,全不选

    在Android开发中,ListView是一种常用的组件,用于展示大量数据列表。在实际应用中,我们经常需要实现ListView的多选功能,比如邮件应用中选择多个邮件进行操作,或者联系人应用中选择多个联系人进行批量删除等。本...

    listview的全选,反选,删除 ,全不选

    listview的每个item都有一个checkbox,解决了setOnCheckedChangeListener()和setonitemclicklistener()的冲突,方法相册时间了listview的全选,反选,删除 ,全不选等功能。

    listview全选、反选、单选、多选、全不选、获取选中数据等.zip

    在实际开发中,我们经常需要实现ListView的各种选择功能,包括全选、反选、单选、多选以及全不选,并能获取选中的数据。下面将详细讲解这些功能的实现方法。 1. 单选: 在ListView中实现单选通常采用RadioGroup配合...

    带CheckBox的listView 支持多选,全选,反选

    全选功能则需要一个额外的全选按钮,点击该按钮时,遍历ListView的所有可见项(或者所有项,如果ListView数据量不大),将它们的CheckBox设为选中状态,并更新选中状态集合。 反选功能相对简单,只需遍历选中状态...

    android listview item列表全选、单选、反选示例源码,无错误

    android listview item列表全选、单选、反选示例源码,无错误、方便实用。欢迎好评!!谢谢! android listview item列表全选、单选、反选示例源码,无错误、方便实用。欢迎好评!!谢谢!

    AndroidListView全选功能,非CheckBox

    `AndroidListView全选功能,非CheckBox`这个主题是关于如何实现ListView中的全选功能,但不使用传统的CheckBox控件。这种实现方式可能是通过自定义Adapter和单个选择状态管理来达到目的。下面将详细介绍这个功能的...

    CheckBox实现多选列表,并实现全选、反选功能

    在Android开发中,CheckBox是一种常用的UI元素,用于提供用户选择多个选项的功能。本文将深入探讨如何使用CheckBox来创建一个多选列表,并实现全选和反选的功能。这些功能常见于各种应用,如设置菜单、购物车等,...

    全选反选

    压缩包中的文件"选中有问题.docx"可能包含了一些关于实现全选和反选时遇到的问题或解决方案的文档,而"ListViewTest"和"ListViewForCheckbox"可能是示例程序或测试用例,它们可能包含了使用复选框(Checkbox)来实现...

    ListView嵌套ListView带多选,全选,反选,选中数量

    在Android中,嵌套ListView意味着在一个ListView的item中再放置另一个ListView。这通常涉及到自定义ListView的Adapter,创建一个复杂的视图结构。每个内部ListView都需要有自己的Adapter来处理其数据。 2. **多选...

    实现listview 单选 多选 反选 全选功能

    实现ListView的单选、多选、反选以及全选功能是常见的需求,尤其是在需要用户进行多项选择的场景下。下面将详细介绍如何实现这些功能。 首先,我们需要一个适配器(Adapter)来填充ListView的数据。适配器通常继承...

    listview反选、全选、全不选、

    在Android开发中,ListView是一种常用的控件,用于展示大量数据列表。它经常与Adapter结合使用,Adapter是连接数据源和视图的桥梁。在ListView中,我们常常会遇到需要实现复选框(CheckBox)的选择功能,包括单个...

    仿58同城android listview中的全选、取消、反选、删除、编辑、完成多种操作

    总之,这个项目涵盖了Android开发中的多个关键知识点,包括ListView的使用、自定义Adapter、事件监听、数据操作以及UI交互设计。通过学习和实践这个示例,开发者可以深入理解如何在实际应用中实现复杂的列表操作功能...

Global site tag (gtag.js) - Google Analytics