Android ListView的中的数据更新时遇到的一些问题的总结

在Android开发过程中,ListView是比较常用的控件,也是遇到问题比较多的控件。这2天开发时遇到些问题,在此做个记录,以作备考,同时也是抛砖引玉,和大家交流一下。

具体的项目中的内容不好直接说,这里简化一下需求:ListView中显示2中类型,一种个ImageView;一种是CheckBox + TextView,点击CheckBox时,TextView的文言会发生变化。具体请见下图:


图比较简陋,就是表明个意思,主要用来说明问题。

ListView里的item类定义如下:

package com.example.listviwtest;

public class ListItem {
	
	public static final int TYPE_CHECKBOX = 0;
	public static final int TYPE_IMAGE = 1;
	public static final int TYPE_CNT = 2;
	
	private int type = 0;        <span style="white-space:pre">			</span>// item类型
	private String title = null; <span style="white-space:pre">			</span>// TextView里需要显示的文言
	private int imageId = R.drawable.ic_launcher; <span style="white-space:pre">	</span>// ImageViw里需要显示的图片
	private boolean isChecked = false;<span style="white-space:pre">	</span>      <span style="white-space:pre">	</span>// CheckBox是否选中
	
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
	
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	
	public int getImageId() {
		return imageId;
	}
	public void setImageId(int imageId) {
		this.imageId = imageId;
	}
	public boolean isChecked() {
		return isChecked;
	}
	public void setChecked(boolean isChecked) {
		this.isChecked = isChecked;
	}
	
	
}

ListView的Adapter定义如下:

package com.example.listviwtest;

import java.util.List;

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.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class ListAdapter extends BaseAdapter {
	private static final String TAG = "ListAdapter";
	
	private List<ListItem> list = null;
	private Context context = null;
	private LayoutInflater inflater = null;
	private ListView listView = null;
	
	public ListAdapter(Context cxt, List<ListItem> list, ListView listView) {
		this.context = cxt;
		this.list = list;
		this.listView = listView;
		inflater = (LayoutInflater) cxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	}

	@Override
	public int getCount() {
		return list.size();
	}

	@Override
	public ListItem getItem(int position) {
		return list.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	ViewHolder holder = null;
	@Override
	public View getView(final int position, View convertView, ViewGroup parent) {
		final ListItem item = getItem(position);
		 
		if (null == convertView) {
			if (ListItem.TYPE_CHECKBOX == item.getType()) {
				convertView = inflater.inflate(R.layout.item_checkbox, null);
				
				holder = new ViewHolder();
				// !!!这里很关键,使用final不仅仅是语法需要,final后,会生成一个新的holder(fHolder),和原来的holder不一样,通过Log,大家可以清楚的看出来。
				// 这样以后,fHolder就和此时生成的convertView建立了绑定关系。
				final ViewHolder fHolder = holder; 
				holder.itemTitle = (TextView)convertView.findViewById(R.id.item_title);
				holder.itemCheckBox = (CheckBox)convertView.findViewById(R.id.item_checkbox);
				holder.itemCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

					@Override
					public void onCheckedChanged(CompoundButton buttonView,
							boolean isChecked) {
						String text = null;
						if (isChecked) {
							text = "Checked " + fHolder.position;
						} else {
							text = "NotChecked " + fHolder.position;
						}
						
						// 通过这里的Log可以清楚地看到position和holder.position,holder和fHolder的不同,
						Log.d(TAG, "in onCheckedChanged, postition=" + position + ", holder.position=" + fHolder.position
								+ ", holder=" + holder + ", fHolder=" + fHolder);
						
						updateView(fHolder.position, text, isChecked);
					}
					
				});
				
				
				convertView.setTag(holder);
				
			} else if (ListItem.TYPE_IMAGE == item.getType()) {
				convertView = inflater.inflate(R.layout.item_image, null, false);
				
				holder = new ViewHolder();
				holder.itemImage = (ImageView)convertView.findViewById(R.id.item_image);
				
				convertView.setTag(holder);
			}
			
		} else {
			holder = (ViewHolder)convertView.getTag();
			
		}
		
		holder.position = position; // 记录holder引用的list item在List<ListItem>中的位置,以便于点击CheckBox时,更新list item的内容。
		if (ListItem.TYPE_CHECKBOX == getItemViewType(position)) {
			holder.itemTitle.setText(item.getTitle());
			holder.itemCheckBox.setChecked(item.isChecked());
		} else if (ListItem.TYPE_IMAGE == getItemViewType(position)) {
			holder.itemImage.setImageResource(item.getImageId());
		}
		
		return convertView;
	}

	@Override
	public int getItemViewType(int position) {
		return getItem(position).getType();
	}
	
	@Override
	public int getViewTypeCount() {
		return ListItem.TYPE_CNT;
	}
	
	// 用于更新ListView里的item,这里是单个item数据更新,数据更新量小,所以不用notifyDataSetChanged()
	void updateView(int itemIndex, String str, boolean isChecked){   
	    int visiblePosition = listView.getFirstVisiblePosition();   
	    View v = listView.getChildAt(itemIndex - visiblePosition);   
	    if (null == v) {
	    	Log.d(TAG, "itemIndex=" + itemIndex + ", visiblePosition=" + visiblePosition);
	    	return;
	    }
	    ListItem item = getItem(itemIndex);
	    item.setTitle(str);
	    item.setChecked(isChecked);
	    
	    ViewHolder holder = (ViewHolder)v.getTag();
	    if (ListItem.TYPE_CHECKBOX == getItemViewType(itemIndex)) {
	    	holder.itemTitle.setText(str);
	    	holder.itemCheckBox.setChecked(isChecked);
	    }
	}   
	
	// 这里使用静态类,和ListAdapter脱离关系,以节省内容。使用ViewHolder的目的是为了便于引用List Item的各种View,避免每次都通过findViewById()方法去取的,以提高效率。
	static class ViewHolder {
		int position = 0; // !!! 这里非常关键,大家可以试试这里不保存item在数据集List<ListItem>里的位置,点击list item的CheckBox后,滚动,大家会发现更新不对的问题。
		CheckBox itemCheckBox;
		TextView itemTitle;
		ImageView itemImage;
	}
	
}

这里直接把数据更新时的解决方法用注释的方法说了出来,大家可以根据注释的内容修改一下代码,就可以看到item更新出错的问题。


备注:Demo下载位置


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值