android 简单易用的ListView 实现多选的解决方案

本文探讨了Android开发中ListView多选混乱的原因,并提出了优化解决方法,避免了控件复用导致的状态混乱,通过调整代码逻辑实现了稳定、高效的多选功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         做android开发久了 ,难免会经常使用ListView ,使用ListView久了 难免会碰到多选的问题,关于多选,做过的应该都了解,会出现选中混乱的问题。以前的解决版本就是使chekedBox不可获取焦点,然后通过点击listview 的item 实现多选,这样倒是勉强解决了,但总觉得很麻烦,另外与我的初衷也是不太符的,我想点的是checkbox,最后却强制让我点成了item,最重要的 如果我需要用onitemClick事件做其他的怎么办,所以想想还是有问题的,但当时也是时间较紧加上对listview加载机制不太了解,也就没再做过多的思考和优化,另外网上貌似也有一些其他的解决方案,因为没有仔细查过所以我也不太了解,暂不去管它。今天又有了这个需求,所以就抽了些时间对这个问题研究了一下。

          对于解决此问题或者说其他任何问题,首要条件就是你要知道是什么造成的,那它到底是什么造成的呢?    说起来比较抽象,所以还是直接看代码吧

     首先先看布局文件, 很简单

        acivity layout 文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >
    
    <Button 
        
        android:text="showCheckedItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="show"
        />
    
   <ListView 
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:id="@+id/listview"
       ></ListView>

</LinearLayout>

  接下来是ListView item 布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
     >
   
<CheckBox 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/cbTest"
    
    />
</LinearLayout>

OK,布局文件就这两个 下面看java代码


  我先建一个Studnet实体类

package com.example.test;

public class Student {
	public boolean isChecked;
	public String name;
	
	public int no;
	public Student(String name,int no)
	{
		this.no = no;
		this.name = name;
		isChecked = false;
	}

}

下面再建一个自定义的adapter

package com.example.test;

import java.util.ArrayList;

import android.content.Context;
import android.util.Log;
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.Toast;


public class TestAdapter extends BaseAdapter{

	private final String TAG = "TestAdapter";
	
	private ArrayList<Student> list;
	private Context context;
	private LayoutInflater inflater;
	
   private boolean  isClick = false;
	public TestAdapter(ArrayList<Student> list, Context context) {
		super();
		this.list = list;
		this.context = context;
		inflater = LayoutInflater.from(context);
	}

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

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return list.get(position);
	}

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

	@Override
	public View getView(final int position, View convertView, ViewGroup parent) {
		final ViewHolder viewHolder;
		if(convertView == null)
		{
			viewHolder = new ViewHolder();
			
			convertView = inflater.inflate(R.layout.test_list_item_layout, null);
			viewHolder.cbTest = (CheckBox)convertView.findViewById(R.id.cbTest);
			
			convertView.setTag(viewHolder);
		}
		else
		{
			viewHolder = (ViewHolder)convertView.getTag();
		}
		
		final Student  stu = list.get(position);
		Log.d(TAG, stu.isChecked+"---"+position);
		viewHolder.cbTest.setText(stu.name);

	
		Log.d(TAG,"--"+position);
		
//		viewHolder.cbTest.setChecked(stu.isChecked);
		viewHolder.cbTest.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				

			
//				Student stu1 = (Student)(viewHolder.cbTest).getTag();
				Log.e(TAG, stu.isChecked+"----"+"no:"+stu.no +"--"+position);
				if(isChecked)
				{
					stu.isChecked = true;
				
				}
				else
				{
				
					stu.isChecked = false;
				}
				
//				Toast.makeText(context, stu.isChecked+"---"+position, Toast.LENGTH_SHORT).show();
			}
		});
		viewHolder.cbTest.setChecked(stu.isChecked);
		return convertView;
	}
	
	private class ViewHolder
	{
		public CheckBox cbTest;
	}

}

          

            看这里,貌似没有什么特别的,也确实没有什么特别的,但是这样确实就不会出现混乱的情况了,仔细观察就会发现, 关键就是这行代码 viewHolder.cbTest.setChecked(stu.isChecked);的位置   在我最初我编写的时候 ,都会把它放到添加监听器之前的位置,也就是setOnCheckedChangeListener这个方法的前面,这样的结果也就像上面说的那样,会出现混乱。但经过我的思考和尝试,我把它换了一个位置 就“神奇”的好 了。那这是为什么呢,简单来说就是为了使cbTest控件(也就是我们放到listview每个item中的checkbox控件)在每次调用setChecked()方法之前 更新它的 OnCheckedChangeListener。也许这时,还会奇怪为什么要这样呢?下面开始阐述 选择混乱造成的原因,以及为什么这样改变后就解决了。我觉得 如果要理解 我下面说的  首先要对listview item正常的加载以及优化后的加载机制比较了解。好,假设,你已经知道了。

       首先我们应该知道造成混乱的根本原因就是 控件的复用,为什么我就不说了,了解加载机制自然就知道了,那我们为什么要复用呢,之所以要复用 ,是为了减少对象的创建,节约资源,但同时也是造成这个问题的根本原因。先回顾一下产生我们操作的过程。首先当我第一次打开界面不进行任何操作,先会加载能显示的item ,没有问题,但item加载完了,我们开始选择更改checkbox状态,也没问题,该选中的确实选中了,通过 输出 可以看到 item对于的Student(就是我上面创建的实体类)对象状态也改变了,一切貌似都很顺利,好,然后我开始滚动,当我们把我们选中的item滚动至消失时,然后再滚动回来,发现我们原来选中的item 又变回原来状态了,最最“诡异”的是就连我们更改了的Student对象的状态也变了,为什么呢? 仔细想想 ,首先当我们把我们选择的控件滚动至消失时,经过我们的优化,该控件还会被复用,就是说在再次执行getView方法时,这次的checkbox控件还是上次的那个控件,同样监听器也还是上次添加的那个监听器, 因为每次执行getView 方法都还会执行setChecked方法  ,同时还会执行监听器中的onCheckedChanged方法,而这个监听器 还是上次添加的那个监听器,里面的Student对象 也是上次操作的对象 这样就无形中又将我们改变了状态的Student对象的状态变了回来,所以当我再次滚动到我们选中的item位置时,就又变回原来的样子了。
 

 以上说的就是按最初的写法为什么会出现混乱的原因,知道原因,那只要为什么改变了下写法就好了的问题,就很容易理解了,我先更新监听器,然后再执行setChecked方法
  时我们每次操作的都是当下的Student对象 不妨碍其他item,所以也就不会混乱了。
   
   
  说了一大堆,也不知道说明白了没有。。。。
 

             下面是测试使用

               

package com.example.test;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ListView;

public class MainActivity extends Activity {

	private ArrayList<Student> list = new ArrayList<Student>();
	
	private ListView listView;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		listView = (ListView)findViewById(R.id.listview);
		
		for(int i=0;i<30;i++)
		{
			Student stu =new Student("student"+i,i);
			
			list.add(stu);
		}
		
		TestAdapter adapter =new TestAdapter(list, this);
		listView.setAdapter(adapter);
	}
	
	public void show(View v)
	{
		for(Student stu:list)
		{
			if(stu.isChecked)
			{
				Log.d(this.getClass().getSimpleName(), stu.name+"被选中");
			}
		}
	}
	


}

OK 就是这些了。 另外 说下 csdn的代码编辑器真难用


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值