自定义listview适配器的getView方法被多次调用,并且没有一致的顺序


162

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

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调用三次?
  • 当我还没有滚动时,这些convertViews来自哪里?

我做了一些研究,但是没有得到一个好的答案,我确实注意到人们正在将这个问题与布局问题联系起来。因此,以防万一,这是包含列表的布局:

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

    <TextView android:id="@+id/pageDetails"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" />

    <ListView android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:drawSelectorOnTop="false" />

</LinearLayout>

以及每行的布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="108dp"    
android:padding="4dp">

<ImageView
    android:id="@+id/thumb"        
    android:layout_width="120dp"
    android:layout_height="fill_parent"        
    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_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentBottom="true"       
    android:singleLine="true" />

<TextView  
    android:id="@+id/date"
    android:layout_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true" 
    android:paddingRight="4dp"       
    android:singleLine="true" />

<TextView
    android:id="@+id/title"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    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>

感谢您的时间

Answers:


271

这不是问题,绝对不能保证getView()调用顺序或调用次数。在您的特定情况下,ListView通过给a 可能会做最坏的事情height=wrap_content。这迫使ListView在布局时从适配器中测量出几个子项,以了解其大小。这就是滚动之前您所看到的所提供ListView的。convertViewsgetView()


10
我觉得这不是很好的理由。如果您希望能够进行更改,那很好,但是当前它无法回答为什么这样做了?
cdpnet 2011年

45
@cdpnet他确实说了为什么这样做,您ListView的高度为wrap_content,因此不确定其高度,因此它使一些孩子成为一种测试仪,以查看将要放入的内容。更改您的内容ListViewfill_parent然后检查您的日志通话次数应大大减少。
Blundell

4
在2.3和4.x时代ListView之间的行为已发生重大变化。收获是,如ListView所设计的那样,您的单元格/ list_items需要是纯视图,并且需要对其进行优化以快速绘制。根据Romain在Google上关于ListView的Google I / O演讲,可能会调用getView只是为了优化视图呈现并丢弃结果。虽然我认为这不像iOS UITableView那样对开发人员友好,但事实就是如此。
卡梅隆·洛厄尔·帕尔默

4
非常感谢!我不明白为什么经常调用getView()。我将wrap_content切换为fill_parent,现在我的应用程序又很快了:)
Julia Hexen 2012年

28
将ListViews设置为layout_height = wrap_content时应发出警告,因为它会导致很多性能问题。
nathanielwolf 2012年

53

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


4
请注意,Fred T指定了LIST VIEW的layout_height ...而不是行,这是我反复尝试的内容
mblackwell8

8
请注意,fill_parent应使用match_parentAPI级别8和更高版本替换。
杰森·阿克森

我认为getView()方法将通过将ListView的width和height设置为match_parent来调用多次,这只能解决性能问题。
Cuong Vo '18

45

当我将layout_width和layout_height都更改为match_parent时,我摆脱了这个问题(仅更改layout_height没有帮助)。


有用的说明请注意是否有嵌套项目。您必须将“最高”一个更改为match_parent。希望它能帮助某人。


一个单词的伙伴“很棒”,但不知道为什么wrap_content会造成问题。这解决了我的问题。
Android Killer,

@AndroidKiller,当您使用时wrap_contentListView不知道有多少列表项成为List的内容,这就是为什么ListView尝试创建它认为适合显示的行数,以及当您使用fill_parent或时match_parentListView认为我的身高是x,我需要n显示行数。
Adil Soomro 2014年

非常感谢 !通过这种行为,图像由于多次调用而随机更改...从一开始就可以。
Chostakovitch 2015年

7

我无法回答您的“为什么”问题,但是对于刺激性的“ ListView项目重复 ”问题(如果您的集合中的项目大于屏幕高度),我绝对可以解决。

如上述许多人所述,将ListVew标记的android:layout_height属性保持为fill_parent

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

希望这对朋友有帮助!!

最好的问候,Siddhant


请谨慎使用Android早期版本(ICS之前的版本)中的ViewHolder模式,因为它可能会导致您出现MemoryLeak异常。可以在ICS +版本中随意使用它。
Vahid Ghadiri 2015年

@Siddhant,谢谢,我花了一天时间弄清楚重复出现图像GridView并进行更改的原因android:layout_heightandroid:layout_width但对我不起作用。但是,使用ViewHolder固定的重复的图像对我来说我经历了这个教程android-vogue.blogspot.com/2011/06/...
尼古拉Podolnyy

4

问题:为什么适配器多次调用getView()?回答:当Listview在滚动时呈现时,将刷新它的视图以及下一个即将出现的视图,为此适配器需要通过调用getView()获得视图。

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

希望它能解决您的查询。


很好的解释。但是match_parent可能是一个更好的选择。
Fattie 2014年

是@JoeBlow,建议从API 2.4 / 3.0开始使用match_parent而不是fill_parent
jitain sharma

4

我在AutoCompleteTextView中的下拉菜单中遇到了同样的问题。我一直在与这个问题作斗争两天,直到我到达这里,然后您向我展示解决方案。

如果我编写dropDownHeight =“ match_parent”,则此问题已解决。现在,问题与UI相关(当您拥有一项时,下拉列表太大),但多次调用(更重要)的问题已得到解决。

谢谢!!


2

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


好的,但是视图没有改变。活动开始时屏幕上有四个行视图,并且没有滚动或任何用户输入,我每行得到三个getView调用...
edzillion 2010年

这个答案是错误的。正确的答案是,使用** wrap_content **会导致对getView的大量调用。
Fattie 2014年

2

我有相同的问题。如果我将身高设置为fill_parent,则每行通常会收到2次调用。但是,如果我将ListView的高度设置为确切的值,比如说300dp,那么每行就会得到一个准确的GetView调用。

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


1
感谢那。有趣的是-这个问题已经休眠了几个月,但在过去的几周中只出现了一些评论。我不再做这个项目,但是由于我有一段时间,我将尝试一下,然后回到这里进行确认。再次感谢。
edzillion 2011年

同样在这里,我每行接到两个电话。第0、1、2、3行,然后100毫秒后又是第0、1、2、3行-在收集有关看到哪些行的统计信息时,这很麻烦
某处某人2014年

2

对于大家谁还是(设置后heightListViewmatch_parent)被卡住了(像我):

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

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

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/something" />

        <ListView
            android:id="@+id/friendsList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

1

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


0

我提出了这个解决方案,也许不是最好的解决方案,但是可以完成工作...

//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;
}

希望能帮助到你!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.