Добавить TextView программно в настраиваемую строку Listview

Я хочу программно добавить один TextView в Linearlayout, объявленный в файле XML, который определяет пользовательскую строку, которая применяется ко всем строкам списка. Для этого у меня есть следующий код:

<LinearLayout
    android:id="@+id/zv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignTop="@+id/title"
    android:layout_marginBottom="1dip"
    android:layout_marginLeft="5dip"
    android:layout_marginRight="5dip"
    android:orientation="vertical"
    android:padding="1dip" >
</LinearLayout>

class ListViewAdapter extends BaseAdapter {

(...)
public View getView(int position, View convertView, ViewGroup parent) {
        View vi=convertView;
        if (convertView == null)
            vi = inflater.inflate(R.layout.row, null);

        TextView tv_nz = new TextView(activity.getApplicationContext());

        LinearLayout zv = (LinearLayout)vi.findViewById(R.id.zv);

        tv_nz.setText("testing...");
        zv.addView(tv_nz);
(...)

return vi;
}

Однако TextView появляется более одного раза в каждой строке. Что я делаю неправильно? Спасибо


person notGeek    schedule 05.03.2013    source источник


Ответы (6)


Что я действительно хочу сделать, так это добавить Textview в определенных условиях и некоторые ImageView в противном случае, поэтому я не могу объявить его в файле XML.

Вы должны использовать несколько макетов, чтобы сделать это правильно, поскольку добавление и удаление представлений в макете строки может быть дорогим и медленным. Вы можете создавать различные макеты в XML или Java, но в своем адаптере вы должны переопределить getViewTypeCount() и getItemViewType(), чтобы указать адаптеру ожидать более одного макета и какой макет использовать для каждой строки.

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


Исходный

Однако TextView появляется более одного раза в каждой строке. Что я делаю неправильно?

Вы не смогли учесть, как ListView перерабатывает свои строки. Это очень подробно объясняется в презентациях Google I/O, таких как Turbo-Charge. Ваш интерфейс.

Возможно, вам следует просто добавить дополнительный TextView в XML-макет вашей строки или использовать несколько макетов. Трудно дать вам однозначный совет, поскольку вы не объяснили, почему хотите добавить TextView.


Это добавляет только один TextView в каждую строку:

public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        // Code here only affects new rows
        convertView = inflater.inflate(R.layout.row, null);

        TextView tv_nz = new TextView(activity.getApplicationContext());
        tv_nz.setText("testing...");
        convertView.addView(tv_nz);
    }
    else {
        // Code here affects only recycled rows
    }

    // Code here affects every row when the ListView is scrolled
    return convertView;
}
person Sam    schedule 05.03.2013
comment
Вы можете использовать android:visibility="gone" для TextView, а затем изменить видимость в коде. - person Todd Sjolander; 05.03.2013
comment
Действительно, добавление TextView в XML и изменение видимости внутри getView() может быть отличным решением, в зависимости от того, как notGeek хочет, чтобы строки вели себя. - person Sam; 05.03.2013
comment
@Sam Большое спасибо за ваш ответ, но проблема в том, что мне нужно динамически добавлять ImageViews в макет, поскольку количество изображений варьируется от строки к строке :) - person notGeek; 05.03.2013
comment
@notGeek Если ImageViews всегда находятся в небольшом диапазоне чисел (например, 1–5), вы все равно можете настроить несколько макетов для самого быстрого кода. Или вы можете просто проверить, сколько объектов ImageView вам понадобится и сколько уже существует (getChildCount()), затем добавьте или удалите соответствующее количество просмотров. (Если у вас могут быть десятки ImageView, рассмотрите возможность ExpandableListView.) - person Sam; 05.03.2013

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

Если вы переключитесь на добавление представления только тогда, когда convertView не равно нулю, вы достигнете того, чего хотите.

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

    // If the row does not exist, lets create it.
    if (convertView == null) {
        convertView = inflater.inflate(R.layout.row, null); 

        // Add the text view to the new view
        tv_nz = new TextView(activity.getApplicationContext());
        tv_nz.setId(R.id.tv_nz);

        convertView.addView(tv_nz);
    } else {
        // The row already exists, lets find the TextView
        tv_nz = (TextView) findViewById(R.id.tv_nz);
    }

    if (tv_nz != null) {
        tv_nz.setText("testing...");
    }

    return convertView;
}

Кстати, вы можете устранить необходимость динамического добавления текстового представления, добавив текстовое представление в R.layout.row.

person jimmithy    schedule 05.03.2013

Кажется, вы безоговорочно добавляете TextView, верно?

Вопрос в том, как вы можете определить единственную строку, в которую она должна быть добавлена. Может с помощью параметра position? Только вы знаете.

person class stacker    schedule 05.03.2013
comment
Это упрощенная версия кода. Что я действительно хочу сделать, так это добавить Textview в определенных условиях и некоторые ImageView в противном случае, поэтому я не могу объявить его в файле XML. Спасибо - person notGeek; 05.03.2013
comment
@notGeek Я объявил бы это в файле XML и использовал бы android:visibility. Но опять же, если вы не опубликуете весь код с возможной ошибкой, вы заставите нас всех гадать, как это уже ясно видно. ;) - person class stacker; 05.03.2013
comment
@notGeek Поскольку вы хотите, чтобы строки имели разные макеты, вы должны переопределить два конкретных метода, я обновил свой ответ. - person Sam; 05.03.2013

Я заметил, что вы добавляете TextView независимо от того, является ли convertView нулевым или нет. Это может объяснить тот факт, что в любой строке может быть несколько TextView виджетов.

Но я согласен с предыдущими ответами - может быть гораздо лучший способ выполнить то, что вам нужно.

person Tyler MacDonell    schedule 05.03.2013

Я предполагаю, что когда представление перерабатывается, к нему уже добавлен TextView, и поэтому, когда вы добавляете еще одно, вы не проверяете, не добавлено ли оно из предыдущего представления. Что-то вроде этого должно помочь:

//after your view inflation
LinearLayout zv = (LinearLayout)vi.findViewById(R.id.zv);
TextView tv_nz;
if(zv.getChildCount() == 0) {
    tv_nz = new TextView(activity.getApplicationContext());
    zv.addView(tv_nz);
} else
    tv_nz = (TextView) zv.getChildAt(0);
tv_nz.setText("testing...");
person dennisdrew    schedule 05.03.2013

Вот пример кода для вас. Прежде всего, вы должны решить, в каком формате вы отправляете данные адаптеру. В моем коде я отправил его как массив.

public class CustomArrayAdapter extends ArrayAdapter<String>
{
    ArrayList<String> list;
    Context mContext; // ADD THIS to keep a context

public CustomArrayAdapter(Context context, int textViewResourceId,
        ArrayList<String> objects)
{
    super(context, textViewResourceId, objects);
    this.mContext = context;
    // Pass the elements in the list
    list = objects;
}

Только после того, как у вас есть определенный порядок данных (например, «список»), вы можете перебирать данные по строкам в списке.

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

    if (row == null)
    {

        // get reference to the activity
        LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();

        // Inflate the custom text which will replace the default text view
        //list_item is a simple lineer layout which contain textView1 in it. 
        row = inflater.inflate(R.layout.list_item, parent, false);
    }

    // Create custom text view to customize

    TextView tv = (TextView) row.findViewById(R.id.textView1);

    // TextView can be customized here or in the xml file
    tv.setText(list.get(position));

    return row;

  }
}

И, наконец, вы можете вызвать пользовательский адаптер следующим образом;

 CustomArrayAdapter adapter = new CustomArrayAdapter(getActivity(),
            R.id.textView1,values);
person Canberk    schedule 05.03.2013