ArrayAdapter的getViewTypeCount和getItemViewType方法

163

请用通俗易懂的语言解释一下 ArrayAdapter 类中的 getViewTypeCount()getItemViewType() 方法的用途。

3个回答

312

这些处理的是当您希望为不同的行使用不同类型的视图时的情况。例如,在联系人应用程序中,您可能希望偶数行在左侧有图片,奇数行在右侧有图片。在这种情况下,您可以使用:

@Override
public int getViewTypeCount() {
    return 2;
}

@Override
public int getItemViewType(int position) {
    return position % 2;
}

该框架使用您的视图类型来决定在您的getView方法中通过convertView交给您哪些视图。换句话说,在上面的示例中,您的偶数行将只获得具有左侧图片以重用的回收视图,并且奇数行将只获得具有右侧图片的视图。
如果列表中的每一行具有相同的布局,则无需担心视图类型。实际上,BaseAdapter.java为所有适配器提供了默认行为:
public int getItemViewType(int position) {
    return 0;
}

public int getViewTypeCount() {
    return 1;
}

这确实为每一行提供了相同的视图类型。

编辑 - 概述一般流程:

  1. 使用适配器将数据绑定到您的 AdapterView 上。
  2. AdapterView 尝试显示对用户可见的项目。
  3. 框架为即将显示的第 n 行调用 getItemViewType
  4. 框架检查其回收视图池中是否有第 n 行类型的视图。由于没有任何视图被回收,因此找不到任何视图。
  5. 为第 n 行调用 getView
  6. 您调用 getItemViewType 来确定应该使用哪种视图类型。
  7. 您使用 if/switch 语句根据所需的视图类型填充不同的 XML 文件。
  8. 填充视图信息。
  9. 返回视图,退出 getView,并将您的行视图显示给用户。

现在,当一个视图通过滚动屏幕而被回收时,它会进入由框架管理的回收视图池中。这些基本上是按视图类型组织的,以便在您的 getView 方法的 convertView 参数中给您正确类型的视图:

  1. 框架再次调用getItemViewType以显示它想要展示的行。
  2. 这次,回收池中有一个适当类型的视图。
  3. 回收的视图作为convertView参数传递给您的getView方法。
  4. 您填充回收的视图,并返回它。

1
@Matthew,感谢您的回答。您能描述一下这些方法是如何被调用的吗?我也不明白这实际上与我在 res/layout 中的数据有什么关联。 - Eugene
1
你可以在getItemViewType方法中自行返回它。 - Matthew
75
注意!getItemViewType() 必须返回 0 到 getViewTypeCount() - 1 之间的整数。 - PacificSky
好的回答,你能分享一下支持你解释的文档吗? - GionJh
非常感谢您的回答。特别是,流程的描述帮助我澄清了脑海中的困惑。 - Solace
显示剩余6条评论

12
如果我们需要在列表视图中显示不同类型的视图,那么最好使用适配器中的 getViewTypeCount()getItemViewType(),而不是切换视图的 View.GONEView.VISIBLE。在 getView() 中执行这样的切换视图操作会非常耗费资源,从而影响列表的滚动。
请查看此链接以了解在适配器中使用getViewTypeCount()getItemViewType() 的用法。
链接:the-use-of-getviewtypecount

1
在getItemViewType()中返回IGNORE_ITEM_VIEW_TYPE将始终将'view'作为null传递给getView(int position, View view, ViewGroup viewGroup),从而强制初始化所有UI组件。这不是一个糟糕的性能指标吗?在使用holder模式的情况下,我们可以重用现有的UI视图,并根据位置简单地切换视图到View.GONE或View.VISIBLE。 - Namrata Bagerwal

12

小心!!!
昨天我遇到了一个问题,实现ListView时,它的两种行视图类型在我滚动后混乱了。尽管此主题中得票最高的答案给出了很好的普遍解释,但它没有强调如何停止上面提到的UI错误这一最重要的信息。

这是我的解释:
BaseAdaptergetView方法使用getViewTypeCount()getItemViewType()来确定应该获取、回收和返回哪种类型的视图(正如主题中顶部的答案所解释的那样)。但是,如果您不按照Android API文档的直觉实现这两个方法,则可能会遇到我提到的问题。

实现的总结指南:
为了实现ListView的多种类型的行视图,我们必须基本上实现getItemViewType()getViewTypeCount()方法。而getItemViewType()的文档给出了以下注释:

注意:整数必须在0getViewTypeCount() - 1的范围内。 还可以返回IGNORE_ITEM_VIEW_TYPE

因此,在getItemViewType()中,您应该为视图类型返回值,从0开始的最后一种类型(类型数-1)。例如,假设您只有三种视图类型?那么,根据用于视图的数据对象,您可以仅从getItemViewType()方法返回 0 1 2 ,就像从零开始的数组索引一样。并且,由于您使用了三种视图类型,您的getViewTypeCount()方法必须返回3。

如果您返回任何其他整数值,如1、2、3或111、222、333,则可能会遇到上述UI错误,因为您没有遵守Android API Doc。

如果您还没有明白或仍然需要进一步了解,请阅读我在此StackOverflow Q&A线程中的详细答案

请查看Android开发者文档,以获取更多信息,您可能能够直接找到线索。

希望这个答案对某些人有所帮助,可以节省很多时间!

干杯!!!


1
感谢提到 IGNORE_ITEM_VIEW_TYPE,我在我的一个ListView中遇到了bug。我使用ArrayAdapter插入了一个由1个图像和2个文本视图组成的视图。我只有一种视图类型。我尝试不重写getItemViewType方法或通过返回位置或硬编码整数来覆盖它。在所有情况下,我的列表中都会出现重复,并且在滚动时会出现奇怪的行为(重复项会发生变化)。在该方法中返回 IGNORE_ITEM_VIEW_TYPE 解决了这个问题。谢谢。 - Alex
@Alex 很高兴对你有所帮助! :-) - Randika Vishman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接