牛骨文教育服务平台(让学习变的简单)

ListView优化问题网上已经有很多资料了,下面只是简单贴一个demo。这里要说一下listview里面checkbox的状态问题。

            holder.cb_vd.setChecked(state.get(position));
         holder.tv_vd.setText(processList.get(position).processName);
         
         holder.cb_vd
                 .setOnCheckedChangeListener(new OnCheckedChangeListener() {

                     @Override
                     public void onCheckedChanged(CompoundButton buttonView,
                             boolean isChecked) {
                         // TODO Auto-generated method stub
                         if (isChecked) {
                             state.put(position, true);
                         } else {
                             state.put(position, false);
                             System.out.println(position+" :"+state.get(position));
                         }
                         
                     }
                 });

通过这段代码进行注册checkbox的监听器,并且进行状态监听发现:当listview每一个item滑出屏幕的时候,上面这个函数都会被调用,并且isCheckde都会设为false!觉得非常的不可思议。然后自己查找了很久资料,发现是上面的话调错位置了。应该是这样写的:

holder.cb_vd
					.setOnCheckedChangeListener(new OnCheckedChangeListener() {

						@Override
						public void onCheckedChanged(CompoundButton buttonView,
								boolean isChecked) {
							// TODO Auto-generated method stub
							if (isChecked) {
								state.put(position, true);
							} else {
								state.put(position, false);
								System.out.println(position);
							}
						}
					});
			
			holder.cb_vd.setChecked(state.get(position));
			holder.tv_vd.setText(processList.get(position).processName);

至于原因的话,下面这段话表达的非常的好!
起因还是得讲到ListView的回收机制。假如我的ListView最多只能显示10个View,那么起初就会调用十次getView构造十个全新的View(包括对其中的checkbox设置监听器)。当我将列表往下拉出现第11个列表项的时候,顶部第一个列表项被隐藏,同样会再调用一次getView,不过此时getView的参数将返回刚刚被隐藏的第一个列表项的View,并对这个View更改数据作为即将出现的第11个View。问题就出在这里,我把checkbox.setChecked()方法调用放在了设置监听器前面,此时因为更改了checkbox的状态,势必引起触发状态更改的监听器。注意!由于第11个View是用被隐藏的第1个View回收来的,虽然还没有执行下一行设置监听器的代码,但实际上它已经拥有了一个状态监听器,这个监听器是这个View还是作为第一个View时设置。那个时候的监听器设置更改的第一项的数据,而不是第11项数据。因此,理所当然不能正确更改第11项数据,反而更改了无辜的第1项数据。如果我把两行代码顺序反过来,先更改监听器,再设置状态,引发的监听器自然也就是新的监听器,逻辑也就对了。
(转自 http://www.jb51.net/article/33424.htm)

下面贴上全部代码:

package com.example.mytesttwo;

import java.util.Hashtable;
import java.util.List;

import android.os.Bundle;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
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.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
	private ListView lv;
	private List<RunningAppProcessInfo> processList;
	private Hashtable<Integer, Boolean> state;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		state = new Hashtable<Integer, Boolean>();
		lv = (ListView) findViewById(R.id.lv);

		ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
		processList = am.getRunningAppProcesses();

		lv.setAdapter(new MyAdapter());
	}

	private class MyAdapter extends BaseAdapter {

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

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

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

		@Override
		public View getView(final int position, View convertView,
				ViewGroup parent) {
			// TODO Auto-generated method stub
			ViewHolder holder = null;

			if (convertView == null) {
				convertView = View.inflate(MainActivity.this, R.layout.iv_item,
						null);
				holder = new ViewHolder();
				holder.tv_vd = (TextView) convertView
						.findViewById(R.id.process_name);
				holder.cb_vd = (CheckBox) convertView.findViewById(R.id.chose);
				convertView.setTag(holder);
				state.put(position, false);
			} else {
				holder = (ViewHolder) convertView.getTag();
				if (state.get(position) == null) {
					state.put(position, false);			
				}
			}
						
			holder.cb_vd
					.setOnCheckedChangeListener(new OnCheckedChangeListener() {

						@Override
						public void onCheckedChanged(CompoundButton buttonView,
								boolean isChecked) {
							// TODO Auto-generated method stub
							if (isChecked) {
								state.put(position, true);
							} else {
								state.put(position, false);
								System.out.println(position);
							}
						}
					});
			
			holder.cb_vd.setChecked(state.get(position));
			holder.tv_vd.setText(processList.get(position).processName);
					
			return convertView;
		}

	}

	private class ViewHolder {
		public TextView tv_vd;
		public CheckBox cb_vd;
	}

}