Пользовательский адаптер Android: элементы списка заполняются только при прокрутке

У меня возникла проблема с правильным заполнением моего списка моим пользовательским ArrayAdapter (код ниже). Насколько я понимаю, мой адаптер заполняет textviewResourceId только тогда, когда он создан, поскольку я использую конструктор Adapter(context, rowLayout, textViewResourceId, ArrayList<Items>), но метод getView вызывается только тогда, когда невидимые строки становятся видимыми.

Это вызывает проблему, так как при первом отображении моего списка отображается только заголовок моей статьи, и мне приходится прокручивать весь список вниз и вверх, чтобы все представления в каждой строке были заполнены правильно (поскольку это задача выполняется в getView).

Может кто-то указать мне верное направление? Как я мог бы реорганизовать это так, чтобы все представления в каждой видимой строке сразу заполнялись?

Код для моего пользовательского адаптера:

import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class ArticleArrayAdapter extends ArrayAdapter<Article> {

    private final Context context;
    private final ArrayList<Article> articles;
    @SuppressWarnings("unused")
    private final int rowLayout;

    public ArticleArrayAdapter(Context context, int rowLayout, int textViewResourceId, ArrayList<Article> articles) {
        super(context, rowLayout, textViewResourceId, articles);
        this.rowLayout=rowLayout;
        this.context = context;
        this.articles = articles;
    }

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

        View row = convertView;
        if (row == null) {
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            row = inflater.inflate(R.layout.affichageitem, null);
        }
        else {
            TextView viewTitre = (TextView)row.findViewById(R.id.titre);
            TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
            TextView viewDate = (TextView)row.findViewById(R.id.date);
            ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
            viewTitre.setText(articles.get(position).getTitle());
            viewAuteur.setText(articles.get(position).getCreator());
            viewDate.setText(articles.get(position).getDate());
            Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
            viewLogo.setImageDrawable(drawLogo);
        }
        return super.getView(position, convertView, parent);
    }
}

Отредактированная версия:

public class ArticleArrayAdapter extends ArrayAdapter<Article> {

    private final Context context;
    @SuppressWarnings("unused")
    private final int rowLayout;

    public ArticleArrayAdapter(Context context, int rowLayout,int textViewResourceId) {
        super(context, rowLayout, textViewResourceId);
        this.rowLayout=rowLayout;
        this.context = context;
    }

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

        View row = convertView;
        if (row == null) {
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            row = inflater.inflate(R.layout.affichageitem, null);
        }
        else {
            TextView viewTitre = (TextView)row.findViewById(R.id.titre);
            TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
            TextView viewDate = (TextView)row.findViewById(R.id.date);
            ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
            viewTitre.setText(getItem(position).getTitle());
            viewAuteur.setText(getItem(position).getCreator());
            viewDate.setText(getItem(position).getDate());
            Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
            viewLogo.setImageDrawable(drawLogo);
        }
        return super.getView(position, convertView, parent); // <<- ONLY TITLES
        //return row; <<- EMPTY
    }
}

рядмакет:

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

    <ImageView
        android:id="@+id/category_logo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:contentDescription="@string/logo_desc"
        android:padding="20dp" />

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:paddingLeft="5dp" >

        <TextView
            android:id="@+id/titre"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textSize="18dp"
            android:textStyle="bold" />

        <LinearLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity=""
            android:orientation="horizontal" >

            <TextView
            android:id="@+id/auteur"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:textSize="12dp" />
            <TextView
            android:id="@+id/espace"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/espace"
            android:adjustViewBounds="true"
            android:textSize="12dp" />
            <TextView
            android:id="@+id/date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:textSize="12dp" />
        </LinearLayout>

    </LinearLayout>

</LinearLayout>

person 2Dee    schedule 11.10.2012    source источник
comment
Вам следует посмотреть, как разработчик Android Ромен Гай обсуждает эффективность адаптеров и getView() для ускорения вашего кода.   -  person Sam    schedule 11.10.2012


Ответы (4)


Проблема в том, что вы не заполняете новые представления. Что происходит, так это то, что Android может сохранять фиксированное количество просмотров, которые будут использоваться для просмотра вашего списка. Представления перерабатываются, поэтому невозможно «заполнить» все ваши представления до того, как они станут видимыми. Эта линия

if (view == null) {
    LayoutInflater inflater = ((Activity)context).getLayoutInflater();
    row = inflater.inflate(R.layout.affichageitem, null);
}

проверяет, перерабатывается ли представление или нет. null означает, что это не так, поэтому, если вы получите null, вам нужно раздуть новое представление. До этого ваш код в порядке. Однако вам необходимо заполнить представление, независимо от того, является ли оно недавно увеличенным представлением или нет. Поэтому у вас не должно быть оператора else, просто

View row = convertView;
if (row == null) {
    LayoutInflater inflater = ((Activity)context).getLayoutInflater();
    row = inflater.inflate(R.layout.affichageitem, null);
}
TextView viewTitre = (TextView)row.findViewById(R.id.titre);
TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
TextView viewDate = (TextView)row.findViewById(R.id.date);
ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
viewTitre.setText(getItem(position).getTitle());
viewAuteur.setText(getItem(position).getCreator());
viewDate.setText(getItem(position).getDate());
Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
viewLogo.setImageDrawable(drawLogo);

return row;

Причина, по которой это работало, когда вы прокручивали до конца, заключается в том, что на обратном пути getView получал переработанный views и перепрыгивал прямо в предложение else, которое у вас было.

person vikki    schedule 11.10.2012

Вы используете собственную коллекцию ArrayList<Article>.

Обратите внимание, что каждый ArrayAdaper<foo> уже имеет встроенный сбор данных, где вы можете добавить add(foo) или addAll(List<foo>) и очистить его методом clear().

Кроме того, ListView может отслеживать эти данные и обновлять их при изменении этих данных. Или явно, когда notifyDataSetChanged() вызывается на адаптере.

Проблема в том, что вы принимаете данные в конструкторе, сохраняете их в еще одной локальной переменной, а notifyDataSetChanged() не вызывается. Вы не можете вызвать его из конструктора, так как объект все еще находится в стадии разработки.

Итак, не принимайте данные в конструкторе. Внутри getView() используйте getItem(position), чтобы получить Article предмет.

Добавьте данные извне, например:

ArticleArrayAdapter adapter = new ArticleArrayAdapter(context,rowLayout,android.R.layout.simple_list_item_1);
adapter.addAll(articles);
myListView.setAdapter(adapter);
person S.D.    schedule 11.10.2012
comment
Спасибо за совет, встроенный сбор данных работает как шарм! Но тем не менее, я получаю ту же проблему с пустыми строками при загрузке новых статей. Мне нужно прокрутить, чтобы увидеть правильный макет. После внесения предложенных вами изменений я попытался в getView как вернуть строки, так и вернуть super.getView(position, convertView, parent). Первый показывает пустые строки, второй показывает только название статьи. Я предполагаю, что проблема заключается в том, что возвращает getView. Должен ли я где-нибудь вызывать notifyDataSetChanged()? Кроме того, каким должен быть textViewResourceId? Один из textViews в rowLayout ? - person 2Dee; 12.10.2012
comment
Не используйте super.getView(), он просто вернет один текст, используйте return row. Проблема может быть в двух местах: в getView() или в макете Xml, который вы раздуваете внутри getView(). - person S.D.; 12.10.2012
comment
Обновленный ответ. используйте android.R.layout.simple_list_item_1 в качестве ресурса по умолчанию. Он покажет простой текст, полученный из toString() каждого объекта Article, если вы не предоставляете настраиваемые представления строк. - person S.D.; 12.10.2012
comment
Я попробовал ArticleArrayAdapter(ListMenu.this, R.layout.rowLayout, android.R.layout.simple_list_item_1), но получил тот же результат. Добавил мой файл макета в вопрос, а также для справки. - person 2Dee; 12.10.2012

Мне кажется правильным - единственное: нет необходимости вызывать суперкласс. Попробуйте вернуть собранный вид следующим образом:

return row;
person Chris Conway    schedule 11.10.2012
comment
Спасибо. Я попробовал ваше предложение, но в результате первые видимые строки были полностью пустыми. - person 2Dee; 12.10.2012
comment
Что говорит Logcat, когда вы распечатываете недостающие данные, например дату? Он появляется? - person Chris Conway; 12.10.2012
comment
Ничего не появляется, пока я не начну прокручивать. Кажется, что getView не вызывается раньше меня. - person 2Dee; 12.10.2012

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

Я расширил MyAdapter из класса BaseAdapter.

mAdapter = new MyAdapter(this, mFinalContactList);
mListView.setAdapter(mAdapter);

Для автоматической прокрутки до последнего со всем созданным списком я использовал следующие строки кода.

mAdapter.notifyDataSetChanged();
mListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
mListView.smoothScrollToPosition(mFinalContactList.size()-1);

Это может помочь тем, кто пытается использовать автоматическую прокрутку до последнего.

Н.Б. Метод mListView.setSelection(position); может указывать на последний элемент списка только в том случае, если мы установили последнюю позицию. Но не может заполнить элемент списка от forst до last.

Спасибо

person Mohammad Zakaria    schedule 06.11.2016