Android ListView с различными макетами для каждой строки - PullRequest
337 голосов
/ 24 января 2011

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

Ответы [ 6 ]

409 голосов
/ 24 января 2011

Поскольку вы знаете, сколько типов макетов у вас будет - эти методы можно использовать.

getViewTypeCount() - этот метод возвращает информацию о количестве типов строк в вашем списке

getItemViewType(int position) - возвращает информацию о том, какой тип макета вы должны использовать, основываясь на позиции

Тогда вы раздуваете макет, только если он нулевой, и определяете тип, используя getItemViewType.

Посмотрите на этот учебник для получения дополнительной информации.

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

  • Хранение представленийв объекте под названием ViewHolder.Это увеличит скорость, потому что вам не придется каждый раз вызывать findViewById() в методе getView.См. List14 в демоверсиях API .
  • Создайте один общий макет, который будет соответствовать всем комбинациям свойств, и скройте некоторые элементы, если у текущей позиции его нет.

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

62 голосов
/ 24 января 2011

Я знаю, как создать настраиваемый адаптер строки + настраиваемый массив для поддержки настраиваемой строки для всего представления списка.Но как один просмотр списка может поддерживать много разных стилей строк?

Вы уже знаете основы.Вам просто нужно, чтобы ваш настраиваемый адаптер возвращал другой макет / представление на основе предоставленной информации о строке / курсоре.

A ListView может поддерживать несколько стилей строк, поскольку он является производным от AdapterView :

AdapterView - это представление , чьи дочерние элементы определяются адаптером.

Если вы посмотрите на Адаптер , вы увидите методы, которые учитывают использование специфических для строки представлений:

abstract int getViewTypeCount()
// Returns the number of types of Views that will be created ...

abstract int getItemViewType(int position)
// Get the type of View that will be created ...

abstract View getView(int position, View convertView, ViewGroup parent)
// Get a View that displays the data ...

Последние два метода обеспечивают позицию так что вы можете использовать это для определения типа представления вы должны использовать для этой строки .


Конечно, вы обычно не используете AdapterView и Adapter напрямую, а скорее используете или наследуете один из их подклассов.Подклассы Adapter могут добавлять дополнительные функции, которые изменяют способ получения пользовательских макетов для разных строк. Поскольку представление, используемое для данной строки, управляется адаптером, хитрость заключается в том, чтобы заставить адаптер возвращать желаемое представление для данной строки. Как это сделать, зависит отконкретный адаптер.

Например, использовать ArrayAdapter ,

  • переопределить getView() для раздувания, заполнить и вернуть желаемый вид для данной позиции.Метод getView() включает возможность многократного использования представлений через параметр convertView.

Но для использования производных от CursorAdapter ,

  • переопределить newView() для раздувания, заполнения и возврата нужного представления для текущего состояния курсора (т.е. текущей "строки") [вам также необходимо переопределить bindView, чтобы виджет могповторное использование представлений]

Однако для использования SimpleCursorAdapter ,

  • определяют SimpleCursorAdapter.ViewBinderс помощью метода setViewValue() для раздувания, заполнения и возврата нужного представления для данной строки (текущего состояния курсора) и «столбца» данных.Метод может определять только «особые» представления и использовать стандартное поведение SimpleCursorAdapter для «нормальных» привязок.

Посмотрите конкретные примеры / руководства для типа адаптера, который вы в конечном итоге используете.

42 голосов
/ 20 апреля 2016

Посмотрите на код ниже.

Сначала мы создадим пользовательские макеты.В этом случае, четыре типа.

even.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#ff500000"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:textColor="@android:color/white"
        android:layout_width="match_parent"
        android:layout_gravity="center"
        android:textSize="24sp"
        android:layout_height="wrap_content" />

 </LinearLayout>

odd.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#ff001f50"
    android:gravity="right"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:textColor="@android:color/white"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:textSize="28sp"
        android:layout_height="wrap_content"  />

 </LinearLayout>

white.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#ffffffff"
    android:gravity="right"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:textColor="@android:color/black"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:textSize="28sp"
        android:layout_height="wrap_content"   />

 </LinearLayout>

черный.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#ff000000"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:textColor="@android:color/white"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:textSize="33sp"
        android:layout_height="wrap_content"   />

 </LinearLayout>

Затем мы создаем элемент списка.В нашем случае со строкой и типом.

public class ListViewItem {
        private String text;
        private int type;

        public ListViewItem(String text, int type) {
            this.text = text;
            this.type = type;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }

    }

После этого мы создаем держатель вида.Настоятельно рекомендуется, поскольку ОС Android сохраняет ссылку на макет для повторного использования вашего элемента, когда он исчезает и снова появляется на экране.Если вы не используете этот подход, каждый раз, когда ваш элемент появляется на экране, ОС Android будет создавать новый, вызывая утечку памяти в вашем приложении.

public class ViewHolder {
        TextView text;

        public ViewHolder(TextView text) {
            this.text = text;
        }

        public TextView getText() {
            return text;
        }

        public void setText(TextView text) {
            this.text = text;
        }

    }

Наконец, мы создаем наш собственный адаптер, переопределяющий getViewTypeCount () и getItemViewType (int position).

public class CustomAdapter extends ArrayAdapter {

        public static final int TYPE_ODD = 0;
        public static final int TYPE_EVEN = 1;
        public static final int TYPE_WHITE = 2;
        public static final int TYPE_BLACK = 3;

        private ListViewItem[] objects;

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

        @Override
        public int getItemViewType(int position) {
            return objects[position].getType();
        }

        public CustomAdapter(Context context, int resource, ListViewItem[] objects) {
            super(context, resource, objects);
            this.objects = objects;
        }

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

            ViewHolder viewHolder = null;
            ListViewItem listViewItem = objects[position];
            int listViewItemType = getItemViewType(position);


            if (convertView == null) {

                if (listViewItemType == TYPE_EVEN) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_even, null);
                } else if (listViewItemType == TYPE_ODD) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_odd, null);
                } else if (listViewItemType == TYPE_WHITE) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_white, null);
                } else {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.type_black, null);
                }

                TextView textView = (TextView) convertView.findViewById(R.id.text);
                viewHolder = new ViewHolder(textView);

                convertView.setTag(viewHolder);

            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            viewHolder.getText().setText(listViewItem.getText());

            return convertView;
        }

    }

И наша деятельность выглядит примерно так:

private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main); // here, you can create a single layout with a listview

        listView = (ListView) findViewById(R.id.listview);

        final ListViewItem[] items = new ListViewItem[40];

        for (int i = 0; i < items.length; i++) {
            if (i == 4) {
                items[i] = new ListViewItem("White " + i, CustomAdapter.TYPE_WHITE);
            } else if (i == 9) {
                items[i] = new ListViewItem("Black " + i, CustomAdapter.TYPE_BLACK);
            } else if (i % 2 == 0) {
                items[i] = new ListViewItem("EVEN " + i, CustomAdapter.TYPE_EVEN);
            } else {
                items[i] = new ListViewItem("ODD " + i, CustomAdapter.TYPE_ODD);
            }
        }

        CustomAdapter customAdapter = new CustomAdapter(this, R.id.text, items);
        listView.setAdapter(customAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView adapterView, View view, int i, long l) {
                Toast.makeText(getBaseContext(), items[i].getText(), Toast.LENGTH_SHORT).show();
            }
        });

    }
}

теперь создайте представление списка внутри mainactivity.xml, как это

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.example.shivnandan.gygy.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <ListView
        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:id="@+id/listView"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"


        android:layout_marginTop="100dp" />

</android.support.design.widget.CoordinatorLayout>
14 голосов
/ 24 января 2011

В своем адаптере массива вы переопределяете метод getView (), как вы, вероятно, знакомы. Затем все, что вам нужно сделать, это использовать оператор switch или оператор if, чтобы вернуть определенный пользовательский View в зависимости от аргумента позиции, переданного в метод getView. Android умен в том, что он даст вам convertView только соответствующего типа для вашей позиции / строки; Вам не нужно проверять, что это правильный тип. В этом вы можете помочь Android, переопределив методы getItemViewType () и getViewTypeCount ().

4 голосов
/ 30 июня 2014

Если нам нужно показать другой тип представления в представлении списка, тогда лучше использовать getViewTypeCount () и getItemViewType () в адаптере вместо переключения представления VIEW.GONE и VIEW.VISIBLE могут быть очень дорогими задачами внутри getView ( ), что повлияет на прокрутку списка.

Пожалуйста, отметьте это для использования getViewTypeCount () и getItemViewType () в адаптере.

Ссылка: use-of-get-getviewtypecount

1 голос
/ 25 августа 2015

ListView был предназначен для простых случаев использования, таких как статическое представление для всех элементов строки.
Поскольку вам необходимо создать ViewHolders и использовать getItemViewType(), а также динамически показывать различные XML-элементы макета элемента строки, вам следует попробоватьДелая это, используя RecyclerView , который доступен в Android API 22. Он предлагает лучшую поддержку и структуру для нескольких типов представлений.

Ознакомьтесь с этим учебным пособием о том, как использовать RecyclerView для того, что вы ищете.

...