listview、gridview单项更新及滑动时数据错乱重复问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ym4189/article/details/77503366

转载请注明出处: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中存入的数据来判断,而不是复用时的直接使用了。

猜你喜欢

转载自blog.csdn.net/ym4189/article/details/77503366