转载请注明出处:http://blog.csdn.net/ym4189/article/details/77503366
前言
listview和gridview原理是一样的,只是显示方式不一样。这里我就以gridview来说明
首先,为什么要单项更新?因为notifyDataSetChanged()方法是刷新整个数据,当我们的数据量很大时,原本只需要刷新一项,但是整个数据都刷新了,这会导致操作不流畅,因此单项刷新很有必要。
单项刷新
我这里的需求是监听gridview的点击事件,当点击某一项时把这一项标记为选择状态,再次点击时取消标记。这里使用List保存需要标记项的索引,当集合中包含当前项时,说明已经标记需要取消标记,反之标记当前项。
public List<String> mSelectedDelete = new LinkedList<String>();
class OnGridItemClickClick implements OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mSelectedDelete.contains(String.valueOf(position))) {
mSelectedDelete.remove(String.valueOf(position));
setItemStatus(position, false);
} else {
mSelectedDelete.add(String.valueOf(position));
setItemStatus(position, true);
}
}
}
}
public void setItemStatus(int position, boolean b) {
View localView = getViewByPosition(position, gridView);
ImageView img = (ImageView) localView.findViewById(R.id.img_state);
if (b) {
img.setVisibility(View.VISIBLE);
} else {
img.setVisibility(View.GONE);
}
}
//根据pos获得此项视图
public View getViewByPosition(int pos, GridView gridView) {
int firstListItemPosition = gridView.getFirstVisiblePosition();
int lastListItemPosition = firstListItemPosition + gridView.getChildCount() - 1;
if (pos < firstListItemPosition || pos > lastListItemPosition) {
return gridView.getAdapter().getView(pos, null, gridView);
} else {
final int childIndex = pos - firstListItemPosition;
return gridView.getChildAt(childIndex);
}
}
数据错乱
可以看到,当我选中了第一个,但是在滑动过程中,没有被选中的项被标记为了选中状态,选中项数据错乱了。(根据情况的不同,数据可以是文本,checkbox的选中状态,图片,背景色,控件的显示状态等等…)
说道数据错乱,首先说一下listview、gridview的复用机制
上图可知listview的缓存优化机制,滚出屏幕的视图会被缓存下来并被复用。这种方法就出现了上述的问题,他会导致滑动时控件的数据错乱。解决也很简单,上面的mSelectedDelete 集合就派上用场了。
看代码:
public class GridViewAdapter extends BaseAdapter {
private Context mContext;
public GridViewAdapter(Context mContext) {
super();
this.mContext = mContext;
}
@Override
public int getCount() {
return imageInfoList.size();
}
@Override
public Object getItem(int position) {
return imageInfoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(this.mContext).inflate(R.layout.gridview_item_file, null, false);
holder.imgState = (ImageView) convertView.findViewById(R.id.img_state);
holder.imgFile = (ImageView) convertView.findViewById(R.id.img_file);
holder.relativeParent = (RelativeLayout) convertView.findViewById(R.id.relative_item);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (imageInfoList != null) {
int width = gridView.getWidth() / 3;
int height = width;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
holder.relativeParent.setLayoutParams(params);
String url = imageInfoList.get(position);
holder.imgFile.setVisibility(View.VISIBLE);
Glide.with(GridViewActivity.this).load(url).fitCenter().into(holder.imgFile);
//判断是否为标记项
if (mSelectedDelete.contains(String.valueOf(position)))
// 显示
holder.imgState.setVisibility(View.VISIBLE);
else
// 隐藏
holder.imgState.setVisibility(View.GONE);
}
return convertView;
}
private class ViewHolder {
RelativeLayout relativeParent;
ImageView imgState, imgFile;
}
}
在这里我的例子是view的显示状态,当然解决思路是一样的。代码中我用一个集合来保存需要标记的item的position值(这里不能直接保存int,要转成string,不然会格式混乱),在getView()中判断此项是否是被标记项来判断是否显示view。
ps:
最好的方式是建一个HashMap来保存两个数据,第一个就是item的position值,这个值表示的是item真正在listview的序号而不是页面上这个item所在的序号。第二个则是这个position的事件情况,可用false,true表示。 当更新数据时,同时更新HashMap里面对应项的状态,然后在getview中根据HashMap里面的对应状态去改变,这样数据事件就是根据HashMap中存入的数据来判断,而不是复用时的直接使用了。