相当新的Android开发人员。
我遇到了一个奇怪的问题,我不确定如何解决。我在这里看到了很多问题,听起来我们遇到了同样的问题,但他们问题的解决方案似乎从未适用于此处发生的事情。
我有一个自定义listView设置使用扩展BaseAdapter并使用视图持有者的单独类(我设置了这个查看自定义列表视图的各种不同示例)。它有一个文本视图,一个按钮,一个进度条和一个图像按钮,这些项目是根据用于下载文件的AsyncTask隐藏/更改的。
当我慢慢滚动列表时,一切正常。当我快速向上/向下滚动列表时,就好像列表中某些单元格的视图被重新分配给其他单元格一样。
我把这行放在我的适配器类中的“getView”方法的顶部:
@Override
public View getView(final int pos, View convertView, ViewGroup parent)
{
Log.i("ks3","getView called, position is " + pos);
我的列表中有7个项目,其中6个适合屏幕。首次显示时,我会在日志中看到:
getView called, position is 0
getView called, position is 1
getView called, position is 2
getView called, position is 3
getView called, position is 4
getView called, position is 5
当我慢慢向下滚动到下一个项目时,接下来打印出来:
getView called, position is 6
当我向上滚动时,这个:
getView called, position is 0
慢慢地向上和向下缓慢地产生这些结果。
当我开始快速来回滚动时,它会多次打印出这条消息,大多数时间显示6和0作为位置,但偶尔我也会看到这些:
getView called, position is 1
getView called, position is 5
不应调用位置1和5,因为它们始终在屏幕上。就好像getView一样混乱了。与此同时,正如我之前所说,我的名单看起来很奇怪。图像按钮将在不应该移动的单元格中向上或向下移动。
如果我回去顺利滚动,我会再次看到0和6的位置。
老实说,我不确定如何解决这个问题。我想也许我可以限制你滚动列表的速度有多快,但是找不到任何有用的东西。
谢谢!
编辑: 我想更新一些关于这个问题的事情。第一个是,从这里的评论和listView上的谷歌视频,我注意到getView可以被调用其他东西然后我想象的那样,例如测量,所以我不应该惊慌通过我最初认为是我的问题的一部分(因为我认为getView被调用错误的位置)。
其次,我多次看到“在适配器中缓存视图”是一个非常糟糕的主意。我不清楚这意味着什么,但我很确定这是我做错的事情之一(我保存了一个按钮实例,progressBar,imageButton ......)。
那个,以及我更新getView以外的视图的事实,我不使用notifyDataSetChanged();这些可能会导致一些不稳定的事情发生。
答案 0 :(得分:13)
ListView
在屏幕上的不同位置重复使用视图是正确的。这是一种优化,通过不分配新的视图来保持内存使用的合理性和快速性。
您可能错误地使用LiewView
。观看this talk on how to properly use ListView
以了解整个故事,但这里是重点:
getView()
方法之外操纵视图(或尝试缓存它们),否则会出现奇怪的行为。getView(int, View, ViewGroup)
的调用提供了一个视图实例,请填充其字段而不是全新的视图。假设您已正确实施getItemType()
,您将始终获得正确的View
类型以重新填充。 getView()
方法,并且只对其他线程进行繁重的工作。getView()
的容器并不一定意味着将显示数据。框架将这些用于测量目的。由于工作可能会被丢弃,这是确保getView()
尽可能快地完成工作的另一个原因。notifyDataSetChanged()
时。不要直接使用视图,它们将在重新绘制时在下一个UI循环中填充。刚刚花了几天时间重新编写一个天真实现的ListView
,我感到很痛苦。但结果是值得的!
答案 1 :(得分:4)
始终在getview中使用此方法: convertView = inflater.inflate(R.layout.listinflate,parent,false);
让您的活动实现onScrollListener,当用户投掷时,通知您的listview适配器,从不介意正确地努力更新项目。当用户不再投掷时告诉适配器注意在getView中提供数据。
这解决了我所有的问题。 还有一件事不在列表视图高度中使用wrap_content。在其中也设置了smoothscroll false。
答案 2 :(得分:1)
我有同样的问题:进入适配器我在getView方法中改变了背景颜色,但有时listview是“reciclyng”视图(得到错误的背景)。 我解决了简单地将“else”放到每个
if(...){changeBackground}
我添加了
else {restore default background}
然后它运作顺利
答案 3 :(得分:0)
getView
。如果你快速滚动你可能会得到奇怪的位置,但这不应该导致你所描述的错误(我猜Android在这里没有错误,因为那些ListViews经过了很好的测试)。例如。当您滚动得足够快时,您可能需要绘制2个视图。
也许您的代码中还有其他问题。
答案 4 :(得分:0)
记录并扩展@Mark的答案
Android完成下一个任务。让我们说我有一个包含100个元素的列表。如果您正在加载,则会调用getview元素0,1,2,3 ...到10。如果我们继续,下一个位置将是11.然而,位置11回收与位置0相同的视图。因此,位置11的视图存在但是它具有错误的值(0值)。
答案:如果视图为空,则修改数据。如果视图为null,则缩小并修改视图的值。如果没有,则修改视图的值。
假设我有一个带有单个textview的item_view(布局),那么适配器应如下所示:
正确的版本:
public class XXXAdapter extends ArrayAdapter<XXX> {....
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
if (convertView==null) {
LayoutInflater inflater = LayoutInflater.from(this.getContext());
convertView = inflater.inflate(R.layout.**itemofthelayout**, parent, false);
}
***Object** item=this.getItem(position);
TextView txt= (TextView) convertView.findViewById(R.id.**idsometextviewinsidelayout**);
txt.setText(String.valueOf(position));
return convertView;
// return super.getView(position, convertView, parent);
}
错误的版本:
public class XXXAdapter extends ArrayAdapter<XXX> {....
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
if (convertView==null) {
LayoutInflater inflater = LayoutInflater.from(this.getContext());
convertView = inflater.inflate(R.layout.**itemofthelayout**, parent, false);
***Object** item=this.getItem(position);
TextView txt= (TextView) convertView.findViewById(R.id.**idsometextviewinsidelayout**);
txt.setText(String.valueOf(position));
return convertView;
// return super.getView(position, convertView, parent);
}
}