Why does getView return wrong convertView objects on BaseAdapter?
2017-09-09 21:14
351 查看
ListView 加载两种不同类型的layout,自定义adapter,重写了getItemViewType和getViewTypeCount方法,代码如下:
看代码没有任何问题,但是每次动态的往data中添加一个元素时,调用setData方法刷新ListView,这是会调用多次adapter.getView方法.
显示出来的效果是“清除历史记录”会被多次添加
通过排查代码,断点都没有找出原因,data数据也是正常的。
只能用最原始的重要的位置输出日志:
在getView给两种类型的convertView设置id,然后输出:
通过日志输出发现最后一次调用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
重新设置LayoutParams导致某些类型丢失,从而导致converView获取错乱。
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; }
相关文章推荐
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及convertView回收的机制
- [置顶] (柯昌合深入研究Android SimpleAdapter BaseAdapter 的getView(int position, View convertView, ViewGroup pa
- Android-BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- Android 重写BaseAdapter碰到getView中convertView出现错误
- BaseAdapter中重写getview的心得以及发现convertView回收的机制 .
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及convertView回收的机制
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- BaseAdapter中重写getview的心得以及发现convertView回收的机制
- (柯昌合深入研究Android SimpleAdapter BaseAdapter 的getView(int position, View convertView, ViewGroup parent)
- BaseAdapter的convertView参数是什么意思
- BaseAdapter几个重载方法介绍isEnabled,getViewTypeCount ,getItemViewType