1. 程式人生 > >listview、gridview單項更新及滑動時資料錯亂重複問題

listview、gridview單項更新及滑動時資料錯亂重複問題

前言

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中存入的資料來判斷,而不是複用時的直接使用了。