【问题标题】:How to avoid refreshing of cells on when calling notifyDataSetChanged() for PinterestLikeAdapterView?为 PinterestLikeAdapterView 调用 notifyDataSetChanged() 时如何避免刷新单元格?
【发布时间】:2013-10-15 03:18:45
【问题描述】:

背景

我正在使用PinterestLikeAdapterView library 显示来自互联网的一些图像,这类似于gridView,但每个单元格的高度不同。

问题

由于我使用这个库来显示来自互联网的图像,因此在调用 notifyDatasetChanged 时不会导致视图混乱是至关重要的。

由于某种原因,调用此函数会调用 getView() 方法,并为视图提供不同的位置。例如,即使我根本没有滚动,并调用 notifyDatasetChanged(或 addAll,如果它是一个 ArrayAdapter),对于位置 0,它将采用位置 8 的视图,对于位置 1,它将采用位置视图7 等等……

这会使整个网格刷新其图像,因此会破坏用户体验。

通常,在gridView和listView中,克服刷新的方法都是将视图使用的位置放在viewHolder中,如果相等,则表示它们仍然匹配。

例如:

... getView(...)
  {
  //<=inflate a new view if needed 
  //avoid refreshing view in case it's still the same position:
  if(position==holder.position)
    return rootView;
  holder.position=position;
  //<=update the view according to its data
  ...
  }

但是,在这里他们以不同的顺序重用其他视图,所以这个技巧在这里不起作用。

由于这个问题,我不仅刷新了几乎所有可见视图,而且由于我使用DiskCacheLru library,它会崩溃,因为它尝试使用 2 个线程将 2 个相同的 inputSteam 数据放入同一个键中。

问题

我能做什么? 这是库中的已知错误吗?

也许我使用了一种不好的方法来克服刷新?

目前,我使用内存缓存来至少获取之前缓存的项目,但这更像是“治愈”而不是“疫苗”...

【问题讨论】:

  • 你的意思是视图顺序很乱?
  • 是和不是。网格以某种方式很好地显示了它们。只是它以奇怪的顺序调用适配器。也许我没有很好地解释这个例子:即使我根本没有滚动,并且我调用了 notifyDataSetChanged() ,getView 方法将调用位置 0 并给我位置 8 的视图而不是真实的视图用于位置 0。问题是正因为如此,我无法克服视图的刷新,所以看起来它重新加载了图像。我认为内存缓存可能会有所帮助,但这并不能真正解决这个问题,因为可能还有其他我没有考虑过的情况。
  • 我一直以为notifyDataSetChanged之后,你不会在getView得到旧的convertViews了。
  • @SherifelKhatib 它也会在这里得到它,但是以一种奇怪的方式 - 对于每个位置,您都会得到错误的视图,因此当它已经与该位置匹配时,您不能使用它的旧状态。这意味着在这种情况下,单元格的更新是不可避免的。
  • 获得不同的视图是完全正常的,但我认为它们应该为空。试试这个技巧: 1- 在里面覆盖 getViewTypeCount()return getCount();。 2- 覆盖其中的getItemViewType(int position)return position;

标签: android android-gridview pinterest notifydatasetchanged


【解决方案1】:

简答

使用像Picasso 这样的图像加载库,它将最近使用的图像缓存在内存中,因此不需要从网络重新加载它们。

长答案:

AdapterView 执行称为View 回收的操作,其中不再需要显示位置的Views 被重新用于显示另一个位置。 (例如,当您向下滚动时,从屏幕顶部消失的 Views 将被重新用于屏幕底部的新位置。)因此,getView() 传递相同的 @987654328 是正常的@ 用于多个位置。

这样做是出于性能原因:膨胀新的Views 很困难并且需要时间,因此AdapterView 尝试尽可能少地这样做。

使用持有人时,您将引用 ImageViewTextView 子项存储在项目的 View 中,因此您不必每次都使用 findViewById() 查找它们 - 您通常不会存储任何特定于特定职位的东西,因为View 及其持有者将经常用于不同的职位。

现在,当您调用notifyDataSetChanged() 时,AdapterView 假定数据集已完全更改。与位置 8 相关联的图像可能不再存在,或者它现在可能与位置 12 相关联。因此,所有现有的Views 都被废弃了——但因为AdapterView 仍然希望避免夸大新的Views,它们被重新用于显示新数据,而不考虑它们之前显示的位置。

这解释了为什么 getView() 会为不同的位置传递相同的 View,以及为什么在调用 notifyDataSetChanged() 时会刷新可见位置。但是如何避免让你的图片刷新,破坏用户体验呢?

使用像Picasso 这样的图像加载库,它将最近使用的图像缓存在内存中,因此不需要从网络重新加载它们。刷新仍会发生,但会是即时的。

【讨论】:

  • 我知道adapterView 的工作方式,并且我使用了viewholder 中的位置来避免刷新已经与数据同步的视图。问题是这个技巧在这个库上不起作用,因为即使没有执行滚动,它也会扰乱视图的顺序。关于你建议的库,它看起来不错,但我不确定它是否有帮助,因为我需要有小尺寸和大尺寸的文件(适合屏幕)所以我下载文件然后做相应地进行下采样。按下图像时,它将显示完整版本。
  • 非常感谢.. !!当我使用毕加索而不是滑翔和通用图像加载器时,它工作正常。
【解决方案2】:

View getView(int position, View view, ViewGroup parent) 将始终在notifyDataSetChanged() 之后以升序方式调用。
我猜是,完成下载任务的顺序会导致这个问题。
正如您在问题中提到的那样,保持职位是避免此问题的好方法。

这是解决它的另一种方法,也可以重用图像视图。

在下载任务中保留每个ImageView 的弱引用。
然后将下载任务包装在一个虚拟的ColorDrawable.
当调用getView 时,将虚拟ColorDrawable 设置为ImageView,然后开始下载。下载完成后,将下载的图片设置回OnPostExecute()中引用的ImageView

说明
http://android-developers.blogspot.jp/2010/07/multithreading-for-performance.html
源码
https://code.google.com/p/android-imagedownloader/source/checkout

【讨论】:

  • 不,任务与此无关。由于 getView 的位置与 viewHolder 的位置不匹配,因此这些任务实际上会被取消。这里发生的事情类似于滚动,即使没有执行滚动。作为旁注,我想更多地了解您提出的想法,但它无法解决我提出的问题。
【解决方案3】:

GitHub中的PinterestLikeListView上有一个很好的例子

这里是图书馆 StaggeredGridView

Android 的实验性 StaggeredGridView 的修改版本。包括自己的 OnItemClickListener 和 OnItemLongClickListener、选择器和固定位置恢复。

你可以在这里获取库项目library

你可以得到Demo项目Here

这是一个非常好的开源项目,所以你可以使用 PinterestLikeAdapterView

希望这个库能帮到你。

【讨论】:

  • 遗憾的是,这个库还有更多问题,例如显示空单元格和 NPE。我已经写了一篇关于搜索显示这种网格的最佳库的帖子,在这里:stackoverflow.com/questions/18557720/…。我发现的所有库都没有错误。每个都有自己奇怪的问题。
【解决方案4】:

似乎这个库的作者已经修复了它,一段时间后我报告了它:

https://github.com/huewu/PinterestLikeAdapterView/issues/8

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多