Why does getView return wrong convertView objects on BaseAdapter?

来源:互联网 发布:淘宝网男装休闲中山 编辑:程序博客网 时间:2024/06/04 00:56

ListView 加载两种不同类型的layout,自定义adapter,重写了getItemViewType和getViewTypeCount方法,代码如下:

class ContentAdapter extends BaseAdapter {        private Context mContext;        private List<String> mData;        /**         * 内容列表         */        private static final int TYPE_CONTENT = 0;        /**         * 清除记录         */        static final int TYPE_CLEAR = 1;        /**         * 类型总数         */        private static final int TYPE_COUNT = 2;        private boolean hasData;        public ContentAdapter(Context context) {            mContext = context;        }        public void setData(List<String> data) {            mData = data;            hasData = Util.isEmpty(data) ? false : true;            notifyDataSetChanged();        }        @Override        public int getCount() {            return getContentCount() + (hasData ? 1 : 0);        }        private int getContentCount() {            return Util.isEmpty(mData) ? 0 : mData.size();        }        @Override        public String getItem(int position) {            if (getItemViewType(position) == TYPE_CONTENT) {                return Util.isEmpty(mData) ? null : mData.get(position);            }            return null;        }        @Override        public long getItemId(int position) {            return position;        }        @Override        public int getItemViewType(int position) {            // 清除历史记录            if (hasData && getCount() - 1 == position) {                return TYPE_CLEAR;            }            return TYPE_CONTENT;        }        @Override        public int getViewTypeCount() {            return TYPE_COUNT;        }        @Override        public View getView(final int position, View convertView, ViewGroup parent) {            ItemHolder holder = null;            int type = getItemViewType(position);            if (type == TYPE_CLEAR) {                if (convertView == null) {                    convertView = LayoutInflater.from(mContext)                                        .inflate(R.layout.layout_clear_item, parent, false);                    new ClearItemHolder(convertView);                }            } else {                if (convertView == null) {                    convertView = LayoutInflater.from(mContext)                                        .inflate(R.layout.layout_item, parent, false);                    holder = new ItemHolder(convertView);                    convertView.setTag(holder);                } else {                    holder = (ItemHolder) convertView.getTag();                }            }            if (type == TYPE_CONTENT) {                if (holder != null && position < mData.size()) {                    String message = getItem(position);                    holder.bindData(message, position);                }            }            return convertView;        }        class ItemHolder {            TextView itemTv;            private String message;            private int position;            public ItemHolder(View rootView) {            // ...            }            public void bindData(String message, int position) {                // ...            }        }        class ClearItemHolder {            TextView itemTv;            public ClearItemHolder(View rootView) {                // ...            }        }    }

看代码没有任何问题,但是每次动态的往data中添加一个元素时,调用setData方法刷新ListView,这是会调用多次adapter.getView方法.
显示出来的效果是“清除历史记录”会被多次添加

通过排查代码,断点都没有找出原因,data数据也是正常的。
只能用最原始的重要的位置输出日志:
在getView给两种类型的convertView设置id,然后输出:

public View getView(final int position, View convertView, ViewGroup parent) {            if (type == TYPE_CLEAR) {                converView.setId(1000);            } else {            converView.setId(1001);            }            Log.d(TAG, "converView:" + converView.getId() + ", position:" + position + ", type:" + type + ", message:"+message);            return convertView;        }

通过日志输出发现最后一次调用getView方法,getCount-2 这行的converView ID(1001)被修改成“清除”layout的converView ID(1000) ,其他输出都是正常。

很诡异的问题,在stackoverflow找到类似问题
https://stackoverflow.com/questions/12018997/why-does-getview-return-wrong-convertview-objects-on-separatedlistadapter

原因:

The recycle bin technically stores the convert views based on the type that was initially read from getItemViewType but is then saved to the view’s LayoutParams object. If you overwrite the view’s LayoutParams you will overwrite the view type and thus break the recycling

重新设置LayoutParams导致某些类型丢失,从而导致converView获取错乱。

修复

在getView在判null基础上,对不同类型加一个tag判断

        public View getView(final int position, View convertView, ViewGroup parent) {            if (type == TYPE_CLEAR) {                if (convertView == null) {                    convertView = xxx;                    new ClearItemHolder(convertView);                }            } else {                if (convertView == null || !(convertView.getTag() instanceof ItemHolder)) {                    convertView = xxx;                    convertView.setTag(holder);                } else {                    holder = (ItemHolder) convertView.getTag();                }            }        // ....            return convertView;        }
阅读全文
0 0
原创粉丝点击