Android BaseAdapter: convertView null при повторном входе getView()

Я создаю ListView с разделами в соответствии с методом, описанным в http://bartinger.at/listview-with-sectionsseparators/ . Но я хотел бы расширить его, повторно используя convertView для элементов, не входящих в раздел. Однако я обнаружил, что переменная convertView имеет значение null каждый раз, когда вводится метод getView(). Может ли кто-нибудь объяснить, почему это так?

ViewHolder holder;
final ListViewItem item = items.get(position);

if (item.isSection()) {
    Section section = (Section)item;

    convertView = inflater.inflate(R.layout.section, null);
    TextView title = (TextView) convertView.findViewById(R.id.section_title);
    title.setText(section.title);
} else {
    if (convertView == null) {
        Log.d("Adapter", "convertView was null");
    }

    Server server = (Server)item;

    convertView = inflater.inflate(R.layout.server_row, null);
    holder = new ViewHolder();
    holder.serverName = (TextView) convertView.findViewById(R.id.server_name);
    holder.serverStatusIcon = (ImageView)convertView.findViewById(R.id.server_status_icon);
    convertView.setTag(holder);

    holder.serverName.setText(server.name);
}

return convertView;

Список создается и отображается без ошибок и прекрасно содержит как разделы, так и не разделы.


person Marc    schedule 20.06.2012    source источник
comment
Где вы это используете? В ListView, ExpandableListView, Gallery, Spinner,...?   -  person K-ballo    schedule 20.06.2012
comment
Если View недоступен для повторного использования, Android передаст значение null в параметр convertView.   -  person AndroidGeek    schedule 11.06.2014


Ответы (3)


Вы правильно реализуете

getItemViewType (int position) ?

См. документацию Android:

Возвращает

Целое число, представляющее тип представления. Два представления должны иметь один и тот же тип, если одно может быть преобразовано в другое в getView(int, View, ViewGroup). Примечание. Целые числа должны находиться в диапазоне от 0 до getViewTypeCount() — 1. Также может быть возвращено значение IGNORE_ITEM_VIEW_TYPE.

Так что, возможно, convertView всегда имеет значение null, потому что адаптер не знает, какие элементы принадлежат друг другу, поэтому он не знает, какие из них проходят переработку...

Попробуй это:

@Override
public int getItemViewType(int position) {
    if (((MyItem)getItem(position)).isHeader()) {
        return 1;
    } else {
        return 0;
    }
}

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

Индекс, который вы возвращаете в getItemViewType, является просто идентификатором для группировки заголовков и не-заголовков вместе.

В этом случае вы должны реализовать метод isHeader (или аналогичный) в ваших элементах модели.

person User    schedule 20.06.2012
comment
Я просмотрел эти методы в API, но там не сказано, что они необходимы. Я ожидал получить любой вид, который я вернул в прошлый раз. Однако я добавил эти методы (как видно из вашего поста) и все еще получаю null convertViews. - person Marc; 20.06.2012
comment
Я предполагаю, что вы не можете получить представление из последнего раза, потому что, скорее всего, вы все еще используете его (предыдущий элемент в списке). Фреймворк решает, когда он передает вам старые представления для повторного использования. Теперь, если вы используете элементы другого типа, фреймворк не может передать вам какое-либо представление, он должен знать, что переработанное (старое) представление совместимо с тем, которое вы создаете. И эти методы помогают в этом. Они не требуются, потому что это ничего не сломает, если вы их не реализуете. Но тогда вы не можете перерабатывать представления и всегда получать нуль. - person User; 20.06.2012
comment
(Это все только моя теория). Но почему бы вам не попробовать реализовать и посмотреть, поможет ли это. - person User; 20.06.2012
comment
Теория неверна, но она помогла мне прийти к ответу (опубликовано). Благодарю вас! - person Marc; 20.06.2012

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

Для полноты картины я добавлю, что если вы создаете несколько типов представлений, getView() возвращает convertView — даже если вы не переопределяете getItemViewType() и getViewTypeCount() — в соответствии с их реализацией по умолчанию (ниже). Конечно, это, скорее всего, не то поведение, которое вам нужно.

public int getItemViewType(int position) {
    return 0;
}

public int getViewTypeCount() {
    return 1;
}
person Marc    schedule 20.06.2012

Я только что прошел через это с GridView, который я создал. У меня были проблемы, когда я пытался назначить преобразованное представление для convertView. Общая структура, которую я принял, была

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

    View newView = null;
    TextView someText;

    // Test to see if there is already a view, if not create one, else use what is
    // already existant in convertView
    if (null == convertView) {
        // inflate your view type into newView here
        newView = myInflater.inflate(R.layout._________);

        // Get all of your subviews you wish to edit here from newView
        someText = (TextView)newView.findViewById(R.id._______);
    }else{
        // Get all of your subviews you wish to edit here from convertView
        someText = (TextView)convertView.findViewById(R.id._______);
    }

    // Perform all re-alignments, view layouts etc... here

    // Perform all updating of subviews data here

    // Return structure
    if (null == convertView) {
        return newView;
    } else {
        return convertView;
    }
}

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

person trumpetlicks    schedule 20.06.2012
comment
Если я правильно понимаю, вы говорите, что проблема может заключаться в том, что самообъявленная переменная View должна использоваться в первый раз. Я проверил это, и, похоже, это не имеет никакого значения. Кажется, я могу без проблем надуть converView. Однако спасибо за предложение. - person Marc; 20.06.2012