ListView с ArrayAdapter и ViewHolder, добавляющими значки к неправильному элементу

У меня есть динамический ListView, который использует ArrayAdapter. Когда имя выбирается из счетчика, имя вместе со значком, показывающим, являются ли они мужчинами или женщинами, добавляются к ListView.

В основном все хорошо (имя корректно добавляется в список вместе со значком). Но значок, показывающий пол, добавляется не к тому элементу в ListView. Имя добавляется в конец списка, а значок помещается рядом с именем вверху списка. Я не знаю, так ли я использую ViewHolder, но на веб-сайте Android.

// Listview inflater
inflater = (LayoutInflater) (this).getSystemService(LAYOUT_INFLATER_SERVICE);

// List Array.
mAdapter = new ArrayAdapter<String>(this, R.layout.player_simple_list, 
                                                 R.id.label, mStrings) {

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

        Log.i("ANDY","View getView Called");
        // A ViewHolder keeps references to children views to 
        // avoid unneccessary calls to findViewById() on each row.
        ViewHolder holder;

        if (null == convertView) {
            Log.i("ANDY","Position not previously used, so inflating");
            convertView = inflater.inflate(R.layout.player_simple_list, null);
            // Creates a ViewHolder and store references to the
            // two children views we want to bind data to.
            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.label);
            holder.icon = (ImageView) convertView.findViewById(R.id.icon);
            if (sexmale == true) {
                holder.icon.setImageBitmap(maleicon);
            }
            else {
                holder.icon.setImageBitmap(femaleicon);
            }
            convertView.setTag(holder);
        } else {
            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
            holder = (ViewHolder) convertView.getTag();

        }
        // Bind the data efficiently with the holder.
        holder.text.setText(getItem(position));
        // Change icon depending is the sexmale variable is true or false.
        Log.i("ANDY","getCount = "+mAdapter.getCount());
        return convertView;
    }
};
setListAdapter(mAdapter);

person andy_spoo    schedule 26.06.2010    source источник
comment
Там есть достоверная документация: developer.android.com/training/improving -макеты/   -  person Vince    schedule 24.10.2014


Ответы (3)


Вы должны установить значки после if-else-if для создания или привязки holder. В противном случае значки правильно отображались бы только в первых нескольких элементах списка, т.е. до тех пор, пока ListView не будет заполнено.

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

    Log.i("ANDY","View getView Called");
    // A ViewHolder keeps references to children views
    // to avoid unneccessary calls to findViewById() on each row.
    ViewHolder holder;

        if (null == convertView) {
            Log.i("ANDY","Position not previously used, so inflating");
            convertView = inflater.inflate(R.layout.player_simple_list, null);

            // Creates a ViewHolder and store references to
            // the two children views we want to bind data to.
            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.label);
            holder.icon = (ImageView) convertView.findViewById(R.id.icon);
            convertView.setTag(holder);
        } else {
            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
            holder = (ViewHolder) convertView.getTag();

        }
        // Bind the data efficiently with the holder.
        holder.text.setText(getItem(position));

        // Change icon depending is the sexmale variable is true or false.
        if (sexmale == true) {
            holder.icon.setImageBitmap(maleicon);
        }
        else {
            holder.icon.setImageBitmap(femaleicon);
        }
        Log.i("ANDY","getCount = "+mAdapter.getCount());
        return convertView;
}
person Primal Pappachan    schedule 26.06.2010
comment
Привет. Я пробовал это, но все значки меняются на один и тот же тип, все в списке. т. е. если выбрано мужское имя, все значки становятся мужскими. - person andy_spoo; 27.06.2010

Вы должны перейти от if через несколько строк данных после комментария, как в этом вопрос объяснен

// Bind the data efficiently with the holder.

так это будет выглядеть так

if (null == convertView) {
    Log.i("ANDY","Position not previously used, so inflating");
    convertView = inflater.inflate(R.layout.player_simple_list, null);
    // Creates a ViewHolder and store references to the two children views
    // we want to bind data to.
    holder = new ViewHolder();
    convertView.setTag(holder);
} else {
    // Get the ViewHolder back to get fast access to the TextView
    // and the ImageView.
    holder = (ViewHolder) convertView.getTag();
}

// Bind the data efficiently with the holder.
holder.text = (TextView) convertView.findViewById(R.id.label);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
if (sexmale == true) {
    holder.icon.setImageBitmap(maleicon);
}
else {
    holder.icon.setImageBitmap(femaleicon);
}
holder.text.setText(getItem(position));
person Pentium10    schedule 26.06.2010
comment
Я не согласен, это просто сделает ViewHolder бесполезным, поскольку вы каждый раз перезаписываете его. Правильное решение должно установить holder.text и holder.icon в ветке if и установить содержимое (setText, setImageBitmap) за пределами блока if. - person cristis; 26.06.2010
comment
Я думаю, что перезапись должна быть там, потому что в противном случае она будет содержать данные, относящиеся к какой-либо другой позиции записи. Например, вы отображаете запись 10, и если вы будете повторно использовать представление кеша из записи 3, если вы не перезапишете набор данных для записи 3, он будет виден для записи 10. - person Pentium10; 26.06.2010
comment
@ Pentium10: нет, Кристис прав. ViewHolder привязан к строке, поэтому виджеты, которые он содержит, не меняются. Что нужно просто изменить, так это содержимое этих виджетов. - person CommonsWare; 26.06.2010
comment
Спасибо за быстрый ответ, очень признателен. Если я вас правильно понял, то я получаю тот же результат, что и мой комментарий ниже. Все значки становятся одного пола по списку. - person andy_spoo; 27.06.2010
comment
Я не вижу, чтобы вы когда-нибудь меняли переменную sexmale, так что всегда одно и то же. - person Pentium10; 27.06.2010
comment
Переменная sexmale изменяется в другом месте программы. Я думаю, что проблема в том, что я не знаю, как получить сохраненное изображение/метку. Мне нужно изображение, эквивалентное тексту:holder.text.setText(getItem(position)); // установить текст из элемента, расположенного в текущей позиции. Пример, приведенный в List14.java:holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2); Я думаю, мне просто нужен соответствующий эквивалент этой строки. - person andy_spoo; 27.06.2010

Обновление: ViewHolder предназначен только для хранения ссылок на представления компонентов внутри макета элемента. Это помогает избежать накладных расходов на вызов findViewById для рендеринга каждого компонента внутри сложных макетов элементов с несколькими компонентами (например, TextView и ImageView в данном случае).

Я исправил это, используя процедуру (называемую getSex) для получения данных о сексе и установки всех данных просмотра, включая значки за пределами блоков if-else.

Рабочий код теперь выглядит так:

if (null == convertView) {
    Log.i("ANDY","Position not previously used, so inflating");
    convertView = inflater.inflate(R.layout.player_simple_list, null);

    // Creates a ViewHolder and store references to the two children views
    // we want to bind data to.
    holder = new ViewHolder();
    holder.text = (TextView) convertView.findViewById(R.id.label);
    holder.icon = (ImageView) convertView.findViewById(R.id.icon);
    convertView.setTag(holder);
} else {
    // Get the ViewHolder back to get fast access to the TextView
    // and the ImageView.
    holder = (ViewHolder) convertView.getTag();
}

// Bind the data efficiently with the holder.
holder.text.setText(getItem(position));
// Change icon depending is the sexmale variable is true or false.
if (getSex (getItem(position)) == true)  {
    holder.icon.setImageBitmap(maleicon);
}
else {
    holder.icon.setImageBitmap(femaleicon);
}
return convertView;
person andy_spoo    schedule 27.06.2010