我有一个ListActivity,它的ArrayAdapter-subclass创建了一个(droid-fu) WebImageView。问题是,当列表变得比在单个页面上显示的更长时,无论调用getView()的是什么,它总是从0开始并计数到4或5(取决于部分行是否可见),即使它实际上在屏幕顶部显示了像#12或14这样的项目...但是onClick()可以正确注册。
换句话说,假设列表中有27行,我让应用程序中到处都是Log.d语句来记录它所做的一切。当ListActivity开始时,我可以看到它使用position = 0、1、2、3、4和5调用getView。如果我触摸其中一行,onClick()也会以正确的位置0..4 (5在屏幕外)触发。到现在为止还好。
现在,将列表向上滑动几行,并观察日志输出。对于position=0、1、2、3、4和5,再一次调用getView()。但是如果我碰到其中一行,onClick()就会在9或14这样的位置触发(实际上,这是正确的)。
换句话说,在此过程中的某个地方,任何负责调用ListAdapter的getView()方法来呈现列表的东西都忘记了它的当前偏移量,并且总是从0开始计数,而不是从屏幕顶部的任何行开始计数。
有没有人知道从哪里开始寻找这里可能出现的问题?也许是关于这些对getView()的调用来自哪里的线索,以及用于该调用的位置arg的值是如何确定的?天哪,有没有可能在getView()的开头放一个断点,然后用Eclipse后退一步,看看对ArrayAdapter的getView()方法的调用是从哪里来的?
更新:忘记了github &现在设法获得了我的程序的副本:
private class ChatInfoAdapter extends ArrayAdapter<ChatInfo> {
private ArrayList<ChatInfo> items;
public ChatInfoAdapter(Context context, int resourceID, ArrayList<ChatInfo> items) {
super(context, resourceID, items);
this.items = items;
// ^^^hmmm... could THIS be the problem somehow?
}
public View getView(int position, View convertView, ViewGroup parent) {
Log.d("getView", "postion=" + position);
View v = convertView;
if (v==null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
ChatInfo info = items.get(position);
// ^^^ grabbing from local "items", not superclass' ArrayList. Hmmm...
if (info != null) {
TextView x = (TextView) v.findViewById(R.id.x);
TextView y = (TextView) v.findViewById(R.id.y);
//...
String url = info.getUrl();
//...
WebImageView wiv = new WebImageView(getBaseContext(), iconUrl, true, arg, arg2);
// ^^^ my slightly-hacked droid-fu WebImageView that takes some extra args
// Even if I completely screwed it up, this class should have no effect on
// the value of "position" when getView gets called at runtime, right?
// Or is there a method of ImageView that might be getting mangled somewhere
// along the line that SHOULD be reporting its position, but instead might
// be returning 0 (which might explain why position always starts from 0).
LinearLayout placeholder = (LinearLayout) v.findViewById(R.id.placeholder);
placeholder.addView(wiv);
v.setTag(info);
}
}
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Log.d("onListItemClick", "position=" + position);
//...
}
}所以..。返回到包含~20+项目的示例列表...当它第一次出现时,我在日志中看到一系列活动,如下所示:
getView: position=0 getView: position=1 getView: position=2 getView: position=3 getView: position=4 getView: position=5 (可能顺序不同,或重复多次)
此时,如果我单击最后一行,我将在日志中看到一条消息,如: onListItemClick: position=4
到现在为止还好。但如果我稍微滚动一下列表,屏幕顶部会显示向下的~2/3项,然后单击屏幕底部的行,我会在日志中看到如下所示的一系列活动:
getView: position=0 getView: position=1 getView: position=2 getView: position=3 getView: position=4 getView: position=5 (可能以不同的顺序,或重复几次) ...onListItemClick: position=20
换句话说,它知道我单击第20行,但是调用getView所显示的位置永远不会高于position=6或position=7,而且它们总是从position=0开始。
(在下面添加了其他评论,并制作了说明问题的视频)
发布于 2010-08-31 14:41:02
您如何确切地检查getView()使用了哪些位置?如果getView()没有接收到正确的位置,ListView将根本无法工作。根据您所说的,您的列表显示了正确的项,因此适配器一定在getView()中接收到了正确的位置。你是在说getChildAt()/getChildCount()吗?
发布于 2011-03-06 20:27:59
首先,我不知道WebImageView是如何工作的,但它似乎有一个回调,在它的实现中,一个方法必须更新一个视图。
使用持有者有点危险,因为如果你将一个持有者绑定到一个方法上(例如,将它设置为final),然后滚动列表,很可能这个持有者正在被另一项重用。在回调中,您正在使用一个持有者,但因为它正被另一个项重用,所以您将更新该项,因此是一个错误的视图。
我建议每个人遵循这一条非常基本的规则:
1.切勿使用由getView()方法检索到的holder对象。当您必须更新视图时,请使用此方法:
private ViewHolder getHolder (int position){
//position is the "real" position in the list
int first = listView.getFirstVisiblePosition ();
int last = listView.getLastVisiblePosition ();
if (position >= first && position <= last){
//The item is visible, so you have to update the view with the changes that you consider
return (ViewHolder)listView.getChildAt (position - first).getTag ();
}else{
//The item is not visible, so you don't have to update any view. If you want you can update the linked item returned by getItem()
return null;
}
}用法:
aMethod (new aListener (){
@Override
public void onCallback (int position){
//Update item if you want
Item item = getItem (position);
...
//Obtain the holder
ViewHolder holder = getHolder (position);
//If it's null you don't have to update anything because the item is not visible
if (holder != null){
//Update view
...
}
});正如您所看到的,回调将位置作为参数返回,但不是必需的,它可以是最终的,并通过方法或类似方法作为参数传递。我已经研究和测试了大约2天(平均16小时)支架是如何工作的,这个方法解决了我所有的问题。
注意:如果您在getView()中,并且想要立即进行更新,则可以直接使用持有符。在回调中,您必须始终使用我的getHolder()方法。
https://stackoverflow.com/questions/3606068
复制相似问题