自定义列表视图适配器 getView 方法被多次调用,并且顺序不一致

Posted

技术标签:

【中文标题】自定义列表视图适配器 getView 方法被多次调用,并且顺序不一致【英文标题】:custom listview adapter getView method being called multiple times, and in no coherent order 【发布时间】:2011-02-06 18:39:55 【问题描述】:

我有一个自定义列表适配器:

class ResultsListAdapter extends ArrayAdapter<RecordItem> 

在覆盖的 'getView' 方法中,我进行打印以检查位置是什么以及它是否是 convertView:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
        System.out.println("getView " + position + " " + convertView);

这个的输出(当列表第一次显示时,还没有用户输入)

04-11 16:24:05.860: INFO/System.out(681): getView 0 null  
04-11 16:24:29.020: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43d415d8  
04-11 16:25:48.070: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.110: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.710: INFO/System.out(681): getView 0 android.widget.RelativeLayout@43d415d8  
04-11 16:25:50.251: INFO/System.out(681): getView 1 null  
04-11 16:26:01.300: INFO/System.out(681): getView 2 null  
04-11 16:26:02.020: INFO/System.out(681): getView 3 null  
04-11 16:28:28.091: INFO/System.out(681): getView 0 null  
04-11 16:37:46.180: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.091: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.730: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43cff8f0  

AFAIK,虽然我找不到明确说明,但 getView() 仅对可见行调用。由于我的应用程序以四个可见行开头,因此至少从 0-3 循环的位置编号是有意义的。但剩下的就是一团糟:

为什么每行调用 getview 三次? 当我还没有滚动时,这些 convertView 是从哪里来的?

我进行了一些研究,但没有得到好的答案,但我确实注意到人们将此问题与布局问题相关联。因此,以防万一,这是包含列表的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_ 
    android:orientation="vertical" >

    <TextView android:id="@+id/pageDetails"
        android:layout_ 
        android:layout_ />

    <ListView android:id="@+id/list"
        android:layout_
        android:layout_ 
        android:drawSelectorOnTop="false" />

</LinearLayout>

以及每一行的布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_    
android:padding="4dp">

<ImageView
    android:id="@+id/thumb"        
    android:layout_
    android:layout_        
    android:layout_alignParentTop="true"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_marginRight="8dp"        
    android:src="@drawable/loading" />

<TextView  
    android:id="@+id/price"
    android:layout_
    android:layout_         
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentBottom="true"       
    android:singleLine="true" />

<TextView  
    android:id="@+id/date"
    android:layout_
    android:layout_         
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true" 
    android:paddingRight="4dp"       
    android:singleLine="true" />

<TextView
    android:id="@+id/title"
    android:layout_
    android:layout_
    android:textSize="17dp" 
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    android:paddingRight="4dp"   
    android:layout_alignWithParentIfMissing="true"
    android:gravity="center" />

</RelativeLayout>

感谢您的宝贵时间

【问题讨论】:

【参考方案1】:

“为什么每行调用 getview 三次?” 因为当您在 listview 上滚动时会调用 getView,并且更好地说是在更改列表视图的位置时调用它!

【讨论】:

好的,但是视图没有改变。活动开始时屏幕上有四个行视图,并且根本没有滚动或任何用户输入,我每行都收到三个 getView 调用... 这个答案是错误的。正确答案是使用 ** wrap_content** 会导致对 getView 的大量调用。【参考方案2】:

这不是问题,绝对不能保证getView() 的调用顺序和调用次数。在您的特定情况下,通过给它一个height=wrap_content,您正在用ListView 做最糟糕的事情。这迫使ListView 在布局时从适配器中测量几个孩子,以了解它应该有多大。这就是为ListView 提供convertViews 的原因,您甚至在滚动之前就看到传递给getView()

【讨论】:

我不认为这是一个很好的理由。如果您希望能够进行更改,那很好,但目前它没有回答为什么会这样做? @cdpnet 他确实说了为什么要这么做,你的ListView 的高度是wrap_content,所以它不确定它的高度,因此它把一些孩子当作一种测试者看看适合什么。将您的 ListView 更改为 fill_parent 然后检查您的日志,呼叫应该会大大减少。 2.3 和 4.x 时代 ListView 之间的行为发生了显着变化。要点是,在设计 ListView 时,您的单元格/list_items 需要是纯视图,并且需要针对快速绘图进行优化。根据 Romain 提出的关于 ListView 的 Google I/O 演讲,可能调用 getView 只是为了优化视图渲染并丢弃结果。虽然在我看来这不像 ios UITableView 那样对开发人员友好,但它就是这样。 非常感谢!我不明白为什么 getView() 被如此频繁地调用。我将 wrap_content 切换为 fill_parent,现在我的应用又快了 :) 将ListViews设置为layout_height=wrap_content时应该有警告,因为它会导致很多性能问题。【参考方案3】:

尝试在列表视图的layout_height 属性上使用match_parent。它将防止getView() 被如此频繁地调用。

【讨论】:

请注意,Fred T 指定了 LIST VIEW 的 layout_height...而不是行,这是我反复尝试的 请注意,对于 API 级别 8 及更高级别,fill_parent 应替换为 match_parent 我认为 getView() 方法会调用多次,通过将宽度和高度设置为 ListView 的 match_parent 只能解决性能问题。【参考方案4】:

我也有同样的问题。如果我将高度设置为 fill_parent,那么我会“通常”每行获得 2 个调用。但是,如果我将 ListView 的高度设置为精确值,比如说 300dp,那么我每行都会得到一个 GetView 调用。

所以,在我看来,唯一的方法是首先确定屏幕的高度,然后以编程方式将 listview 的高度设置为该值。我不喜欢它。希望有更好的办法。

【讨论】:

谢谢。这很有趣——这个问题已经沉睡了几个月,只是在过去的几周里出现了一些 cmets。我不再做这个项目,但因为我有时间我会尝试一下,然后回到这里确认。再次感谢。 这里也一样,我每行接到两个电话。第 0,1,2,3 行,然后 100 毫秒后又是 0,1,2,3 行 - 在收集有关所见行的统计信息时,这是一件很痛苦的事【参考方案5】:

我无法回答您的“为什么”问题,但我绝对有办法解决烦人的“ListView 项目重复”问题(如果您的收藏中的项目更多比屏幕高度)。

正如上面许多人提到的,将 ListVew 标记的 android:layout_height 属性保留为 fill_parent

关于 getView() 函数,解决方案是使用一个名为 ViewHolder静态类。查看this 示例。它成功地完成了在 Array 或 ArrayCollection 中添加所有项目的任务。

希望对朋友有帮助!!

最好的问候, 悉达特

【讨论】:

在 android 的早期版本(ICS 之前)中使用 ViewHolder 模式时要谨慎,因为它可能会导致 MemoryLeak 异常。随意在 ICS+ 版本中使用它。 @Siddhant 谢谢我花了一天的时间来找出在我的GridView 中重复图像并更改android:layout_heightandroid:layout_width 的原因对我不起作用。但是为我使用ViewHolderfixed 重复图像,我完成了本教程android-vogue.blogspot.com/2011/06/…【参考方案6】:

当我将 layout_width 和 layout_height 都更改为 ma​​tch_parent 时,我解决了这个问题(仅更改 layout_height 没有帮助)。


有用的提示注意是否有嵌套项。您必须将“最高”更改为 ma​​tch_parent。希望它可以帮助某人。

【讨论】:

一个单词好友“真棒”,但不知道为什么 wrap_content 会产生问题。这解决了我的问题。 @AndroidKiller 当你使用wrap_content,然后ListView 不知道有多少列表项成为List 的内容,这就是ListView 尝试创建尽可能多的行的原因适合显示,当你使用fill_parentmatch_parent时,ListView认为,好吧,我的身高是x,我需要n显示的行数。 非常感谢!由于多次调用,图像会随机变化......现在从一开始就可以了。【参考方案7】:

问题:为什么 Adapter 会多次调用 getView()? Ans:当 Listview 在滚动时呈现是用下一个即将到来的视图刷新它的视图,对于 哪个适配器需要通过调用 getView() 来获取视图。

问题:如果列表视图的宽度和高度设置为fill_parent,为什么它的调用更少? Ans: 因为充气机对于列表的屏幕区域具有固定大小,所以它计算一次 将视图渲染到屏幕上。

希望它能解决您的问题。

【讨论】:

很好的解释。但 match_parent 可能是更好的选择。 是的@JoeBlow,建议从 API 2.4/3.0 开始使用 match_parent 而不是 fill_parent【参考方案8】:

我在 AutoCompleteTextView 中的下拉菜单中遇到了同样的问题。我与这个问题斗争了两天,直到我到达这里,你告诉我解决方案。

如果我写 dropDownHeight="match_parent" 问题就解决了。现在问题与 UI 有关(当您有一个项目时,下拉菜单太大)但多次调用的问题(更重要)已得到修复。

谢谢!!

【讨论】:

【参考方案9】:

对于所有仍然(在将ListViewheight 设置为match_parent 之后)卡住的人(就像我一样):

您还必须将父布局的height 设置为match_parent

请参见下面的示例。 LinearLayout 是这里的父级:

<LinearLayout
        android:layout_
        android:layout_
        android:orientation="vertical">

        <TextView
            android:layout_
            android:layout_
            android:text="@string/something" />

        <ListView
            android:id="@+id/friendsList"
            android:layout_
            android:layout_ />
    </LinearLayout>

【讨论】:

【参考方案10】:

这可能来晚了,但如果您使用layout_weight,请记住始终设置layout_width="0dp"

【讨论】:

【参考方案11】:

我做了这个解决方案,也许不是最好的,但确实有效......

//initialize control string
private String control_input = " ";

那么 =

@Override
public View getView(int position, View convertView, ViewGroup parent) 

    View gridview = convertView;

    // change input_array for the array you use
    if (!control_input.equals(input_array[position])) 
        control_input = input_array[position];

        //do your magic

     

    return gridview;

希望对你有帮助!

【讨论】:

以上是关于自定义列表视图适配器 getView 方法被多次调用,并且顺序不一致的主要内容,如果未能解决你的问题,请参考以下文章

多次调用getView方法

listview 项目未在自定义适配器的 getview 方法中显示分配的值

getView() 在我的自定义列表视图中不起作用

NullPointerException 自定义列表视图适配器

如何在列表视图的适配器的 getView 中获取上下文

未调用自定义适配器 getView() 方法