пользовательский метод 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 вызывается для каждого ряда три раза?
  • Откуда приходят эти convertView, когда я еще не прокрутил?

Я сделал небольшой поиск и, не получив хорошего ответа, заметил, что люди связывают эту проблему с проблемами компоновки. Так что в случае, вот макет, который содержит список:

<?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>

Спасибо за уделенное время

edzillion
источник

Ответы:

271

Это не проблема, нет абсолютно никакой гарантии ни на порядок, в котором getView()будет звонить, ни на сколько раз. В вашем конкретном случае вы делаете худшее, что возможно, с ListViewпомощью a height=wrap_content. Это заставляет ListViewотмерить несколько дочерних элементов адаптера во время разметки, чтобы понять, насколько большим он должен быть. Это то, что обеспечиваетListView что convertViewsвы видите переданным getView()еще до прокрутки.

Ромэн Гай
источник
10
Я не считаю это веской причиной. Это хорошо, если вы хотите иметь возможность измениться, но в настоящее время он не отвечает, почему он так поступает?
cdpnet
45
@cdpnet Он сказал , почему он делает это, ваши ListViewимеет высоту wrap_content, так что не уверена в его высоту поэтому лежит несколько детей, как своего рода тестер , чтобы увидеть , что будет вписываться в. Измените ваш , ListViewчтобы fill_parentзатем проверить журналы звонки должны быть резко сокращены.
Бланделл
4
Поведение между 2.3 и 4.x эрой ListView значительно изменилось. Вывод заключается в том, что при разработке ListView ваши ячейки / list_items должны быть чистыми представлениями и должны быть оптимизированы для быстрого рисования. Согласно докладу Google I / O о ListView, где представлен Romain, getView может быть вызван просто для оптимизации рендеринга представления и отклонения результата. Хотя, на мой взгляд, это не так удобно для разработчиков, как iOS UITableView, но так оно и есть.
Кэмерон Лоуэлл Палмер
4
Спасибо вам большое! Я не понимаю, почему getView () вызывается так часто. Я переключил wrap_content на fill_parent, и теперь мое приложение снова работает быстро :)
Юлия Хексен,
28
При установке ListViews на layout_height = wrap_content должно быть предупреждение, поскольку это вызывает очень много проблем с производительностью.
Натаниэльвольф
54

Попробуйте с match_parentпомощью layout_heightсвойства списка. Это помешает getView()так часто звонить.

Фред Т
источник
4
Обратите внимание, что Фред Т задает layout_height LIST VIEW ... не строку, что я пытался много раз
mblackwell8
8
Обратите внимание, что fill_parentследует заменить match_parentна API уровня 8 и выше.
Джейсон Аксельсон
Я думаю, что метод getView () будет вызываться несколько раз, устанавливая ширину и высоту, так как match_parent для ListView только решает проблему производительности.
Куонг Во
45

Я избавился от этой проблемы, когда изменил layout_width и layout_height на match_parent (изменение только layout_height не помогло).


Полезное примечание, обратите внимание , если у вас есть вложенные элементы. Вы должны изменить самый высокий на match_parent . Надеюсь, это кому-нибудь поможет.

Евгений Чумак
источник
одно слово приятель "круто", но не знаю, почему wrap_content создает проблему. Это решило мою проблему.
Android Killer
@AndroidKiller при использовании wrap_content, то ListViewне знаю , сколько элемента списка есть , чтобы стать содержанием Листа, то почему ListViewпытается создать стольких строк , как он думает , что пригодны для отображения, а также при использовании fill_parentили match_parent, ListViewдумает, хорошо, мой рост это xи мне нужно nнесколько строк , чтобы показать.
Адиль Соомро
Спасибо вам большое ! При таком поведении изображения менялись случайным образом из-за нескольких вызовов ... Теперь это нормально с самого начала.
Хостакович
7

Я не могу ответить на ваш вопрос «почему», но у меня определенно есть решение проблемы раздражающих элементов ListView, повторяющихся » (если в вашей коллекции есть элементы, размер которых превышает высоту экрана).

Как уже упоминали многие люди, оставьте свойство android: layout_height тега ListVew как fill_parent .

Что касается функции getView (), решение состоит в том, чтобы использовать статический класс с именем ViewHolder . Проверьте этот пример. Он успешно выполняет задачу добавления всех элементов в массив Array или ArrayCollection.

Надеюсь, это поможет друзьям!

С наилучшими пожеланиями, Сиддхант

Siddhant
источник
Будьте осторожны с использованием шаблона ViewHolder в ранней версии Android (до ICS), потому что это может привести к исключению MemoryLeak. не стесняйтесь использовать его в версиях ICS +.
Вахид Гадири
@Siddhant спасибо, я провожу день, чтобы выяснить причину повторения изображений в моем GridViewи изменения android:layout_heightи android:layout_widthне работает для меня. Но используя ViewHolderфиксированные повторяющиеся изображения для меня, я прошел этот урок android-vogue.blogspot.com/2011/06/…
Николай Подольный
4

Вопросы: почему адаптер вызывает getView () много раз? Ответ: Поскольку визуализация Listview при прокрутке обновляет его представление следующими предстоящими представлениями, для которых адаптер должен получать представления, вызывая getView ().

Ques: Почему он вызывает меньше, если ширина и высота списка просмотра установлена ​​в fill_parent? Ответ: Поскольку инфлятор имеет фиксированный размер области экрана для списка, он рассчитывает один раз для визуализации представлений на экране.

Надеюсь, это разрешит ваш запрос.

Джитайн Шарма
источник
Отличное объяснение. Но match_parent может быть лучшим выбором.
Толстяк
Да @JoeBlow, рекомендуется использовать match_parent вместо fill_parent с API 2.4 / 3.0 и выше
jitain sharma
4

У меня была такая же проблема с выпадающим в AutoCompleteTextView. Я боролся с проблемой в течение двух дней, пока я не приехал сюда, и вы не показали мне решение.

Если я напишу dropDownHeight = "match_parent", проблема исправлена. Теперь проблема связана с пользовательским интерфейсом (когда у вас есть один элемент, раскрывающийся список слишком велик), но проблема множественных вызовов (гораздо важнее) устранена.

Спасибо!!

По Арландис Мартинес
источник
2

"Почему getview вызывается для каждого ряда три раза?" Потому что getView вызывается, когда вы прокручиваете список, и лучше сказать, что он вызывается при изменении позиции представления вашего списка!

Унгуряну Ливиу
источник
хорошо, но вид не изменился. когда начинается действие, на экране четыре просмотра строк, и без прокрутки или какого-либо пользовательского ввода я получаю три вызова getView для каждой строки ...
edzillion
Этот ответ неверен. Правильный ответ заключается в том, что использование ** wrap_content ** вызывает огромное количество вызовов getView.
Толстяк
2

У меня та же проблема. Если у меня установлена ​​высота fill_parent, то я получаю «обычно» 2 вызова на строку. Но если я установлю высоту моего ListView на точное значение, скажем, 300dp, то я получу ровно один вызов GetView на строку.

Поэтому мне кажется, что единственный способ - сначала определить высоту экрана, а затем программно установить высоту listvilew на это значение. Мне это не нравится Я надеюсь, что есть лучший способ.

Бобетко
источник
1
Спасибо за это. Забавно - этот вопрос уже несколько месяцев бездействует, и за последние несколько недель появилось несколько комментариев. Я больше не занимаюсь этим проектом, но так как у меня есть некоторое время, я собираюсь попробовать и затем вернуться сюда, чтобы подтвердить. Спасибо еще раз.
edzillion
то же самое здесь, я получаю два звонка на строку. Ряды 0,1,2,3, а затем через 100 мсек еще 0,1,2,3 - что является болью в заднице при сборе статистики о том, какие строки видны
кто-то где-то
2

Для всех, кто до сих пор (После установки heightиз ListViewкmatch_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>
B001 ᛦ
источник
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;
}

Надеюсь, поможет!

Яго
источник