android gridview不使用viewholder直接添加view导致乱序、重复响应的解决方法
要实现可拖动的九宫格,网上搜了下,搜到比较靠谱的是这个版本,原理、实现都写的很清楚,但有个问题,用到GridView时,写adapter时一般都会使用ViewHolder的方式,即adapter使用BaseAdapter,adapter中保存数据,而调用getView方法时,若是第一次调用则将可复用的数据保存到一个ViewHolder类中,后续就不用再重新创建View;或者,adapter中每次根据数据创建view。
由于公司所用的客户端框架限制,我在实现可拖动的九宫格时,在getView方法中,拿到的数据就是一个个已经创建好的View对象,但按照上面提到的xiaanming的拖动实现方式,我将这些已有的View对象在getView中返回、加入到GridView后,发现显示没问题,但是拖动之后,View的显示顺序会乱掉,而且会出现第一次点击某个View时对应回调方法没有响应,但第二次点击时则会响应第一次点击、第二次点击两个回调方法。
为解决这问题,在github上发现如下可拖动九宫格的另一种实现,尝试重写adapter类后解决了该问题,发现这个跟stableid有关。我一开始写的adapter大概样子如下;
1 | public class GridAdapter extends BaseAdapter{ |
这种写法对于ViewHolder方式或是每次创建新View放的方式实现adapter来说,没有任何问题,但由于我直接使用现有的View对象,而GridView本身又有缓存机制,导致GridView重用View对象时View的顺序和我已经创建的viewList不一致,为解决这一问题,就得说下getItemId(int pos)方法和hasStableIds()方法,可以参考这个帖子,引用一下人家的话:
getItemId是干嘛用的?在调用 invalidateView()时,ListView会刷新显示内容。如果内容的id是有效的,系统会跟据id来确定当前显示哪条内容,也就是firstVisibleChild的位置。id是否有效通过hasStableIds()确定。
我的理解是,在BaseAdapter中,hasStableIds()方法默认返回false,意思是每个item元素的id不稳定,会根据item位置来确定id;如果返回true,表示每个item元素的id是稳定的,即相同的id引用相同的对象。这是因为默认id不稳定,导致我上面写的adapter在进行拖拽操作后,item元素(view对象)位置变了,但其id并没有与我在重排列viewList时一致,导致很古怪的乱序或是重复响应问题。参考可拖动九宫格的另一种实现 后,修改adapter实现如下:
1 | private HashMap<Object, Integer> mIdMap = new HashMap<Object, Integer>(); |
说实话,这问题很诡异,和同事一起找,找到GitHub上的九宫格不同实现,看人家代码、做尝试才解决这问题,GridView这控件太复杂,之前还遇到过GridView第一个item不响应的问题(stackoverflow上一堆相关问题),有时间再细说。上面有关BaseAdapter的分析仅仅是我参考网上帖子做的一些推断,如有错误或是疏漏欢迎大家指正。