Адаптеры
В Android часто используются адаптеры. Если говорить в общих чертах, то адаптеры упрощают связывание данных с элементом управления. Адаптеры используются при работе с виджетами, которые дополняют android.widget.AdapterView: ListView, ExpandableListView, GridView, Spinner, Gallery, а также в активности ListActivity и др. Сам AdapterView дополняет android.widget.ViewGroup.
Итак, у нас есть набор объектов и есть компонент View. Назначение адаптера заключается в том, чтобы предоставлять дочерние виды для контейнера. Адаптер берет данные и метаданные определенного контейнера и строит каждый дочерний вид. Например, мы формируем пункты списка (массив строк) и передаём его списку ListView.
Вы что-нибудь поняли? Мой кот тоже ничего не понял. Попробуем объяснить по-другому. Что такое вообще адаптер? Это переходник между двумя какими-то предметами. Допустим, между питьевой водой и котом требуется адаптер в виде крана.
В данном случае адаптер спроектирован плохо, приходится изворачиваться.
Однако вернёмся к Android. В приложениях очень часто используется список на основе ListView. Сам список состоит из множества элементов TextView, которые идут друг за другом. Но их количество будет зависеть от того, чтобы мы хотим отобразить. Если нам нужны дни недели, то достаточно семи элементов, если месяцы, то уже двенадцать, ну а если нам нужны имена котов в Кот д’Ивуаре, то счёт пойдет на сотни. Короче говоря, нам нужно составить данные, например, в виде массива и скормить его списку. Адаптер этим и занимается. Он берёт по порядку предоставленные данные и размещает их в списке по порядку. При этом адаптер на лету создаёт нужные компоненты TextView и помещает в него приготовленный текст. Данные могут быть находиться не только в массиве, но и в базе данных. Для такого случая используется другой адаптер. А также вы можете придумать свой адаптер. Существуют уже готовые адаптеры на самые распространённые случаи и их предназначение можно определить по именам. Например, ArrayAdapter использует массив, а CursorAdapter работает с объектом Cursor, используемый в базах данных.
Готовые адаптеры
Все адаптеры, содержащиеся в Android, дополняют базовый адаптер BaseAdapter. Вот список готовых адаптеров:
- ArrayAdapter — предназначен для работы с ListView. Данные представлены в виде массива, которые размещаются в отдельных элементах TextView
- ListAdapter — адаптер между ListView и данными. Строго говоря, это класс-интерфейс, который можно использовать и в ArrayAdapter и в SimpleAdapter и т.д.
- SpinnerAdapter — адаптер для связки данных с элементом Spinner. Это тоже интерфейс, как ListAdapter и работает по схожему принципу
- SimpleAdapter — адаптер, позволяющий заполнить данными список более сложной структуры, например, два текста в одной строке списка.
- SimpleCursorAdapter — дополняет ResourceCursorAdapter и создаёт компоненты TextView/ImageView из столбцов, содержащихся в курсоре. Компоненты определяются в ресурсах
- CursorAdapter — предназначен для работы с ListView, предоставляет данные для списка через курсор, который должен иметь колонку с именем «_id»
- ResourceCursorAdapter — этот адаптер дополняет CursorAdapter и может создавать виды из ресурсов
- HeaderViewListAdapter — расширенный вариант ListAdapter, когда ListView имеет заголовки.
- WrapperListAdapter — еще один адаптер для списков.
BaseAdapter
Стандартные адаптеры не всегда покрывают потребности программиста. Если вам нужен свой собственный адаптер, то в Android есть абстрактный класс BaseAdapter, который можно расширить. Собственный адаптер необходим в тех случаях, когда требуется специальное управление данными или дополнительный контроль над отображением дочерних представлений. Кроме того, вы можете предусмотреть в своём адаптере элементы кэширования для повышения производительности работы.
Пример использования адаптере на основе BaseAdapter можно увидеть при создании GridView с картинками и в других примерах.
У BaseAdapter есть несколько методов, которые следует переопределить. Например, метод getCount() позволяет узнать количество выводимых объектов.
Другой важный метод адаптера — getView(), который отвечает за создание отдельных элементов списка. Он вызывается для каждого элемента списка, чтобы определить, какие данные нужно отобразить. Метод getVew() содержит параметр convertView, который позволяет использовать заново уже существующий элемент списка, который не отображается, т.к. пользователь пролистнул его с видимой части дисплея. Если convertView не пустой, он может быть использован заново, чтобы не грузить разметку списка. Подобный подход способствует увеличению производительности.
Метод getView() возвращает View, который фактически является контейнером ViewGroup и содержит в себе другие компоненты, например, ImageView или TextView.
На сайте представлены (будут представлены) практически все примеры с адаптерами. Оставайтесь на связи!
Источник
ArrayAdapter
Создание адаптера
ArrayAdapter является простейшим адаптером, который специально предназначен для работы с элементами списка типа ListView, Spinner, GridView и им подобным. Создать адаптер этого вида можно так:
В параметрах используется контекст, XML-разметка для отдельного элемента списка и массив данных. Контекстом может быть сама активность (this), под разметкой подразумевается компонент, в котором выводится текст, например, TextView, а данными является подготовленный массив, все элементы которого по очереди вставляются в указанную разметку.
Разметку можно создать самостоятельно, а можно использовать готовую системную разметку. Если посмотреть на исходники файла simple_list_item_1.xml в документации Android SDK, то увидим, что он содержит TextView. В этом коде мы создали адаптер ArrayAdapter, в котором данные элемента TextView представлены в виде строк.
Чтобы код был более читаемым, можно сделать ещё так:
Мы вынесли массив строк в отдельную переменную.
Используем ресурсы
Если у нас есть готовый файл ресурсов (массив строк), то можно использовать специальный метод createFromResource(), который может создать ArrayAdapter из ресурсов:
Подготовим массив строк:
Теперь мы можем воспользоваться адаптером и применить к Spinner:
В этом примере мы использовали системную разметку android.R.layout.simple_spinner_item, которая тоже содержит TextView.
При использовании этого метода вы можете применять локализованные версии, что очень удобно.
Или можно пойти другим путём. Получить массив из ресурсов и вставить его в адаптер, как в самом первом примере.
Динамическое наполнение
Также мы можем создать массив программно.
ListAdapter
ListAdapter является интерфейсом. По сути ничего не меняется. Заменяем ArrayAdapter adapter на ListAdapter adapter и получаем тот же результат.
Данный интерфейс может пригодиться при создании собственного адаптера, а для стандартных случаев выгоды не вижу.
SpinnerAdapter
SpinnerAdapter также является интерфейсом и может использоваться при создании собственных адаптеров на основе ArrayAdapter. В стандартных ситуациях смысла использования его нет. Вот так будет выглядеть код:
Переопределяем адаптер
По умолчанию ArrayAdapter использует метод toString() из объекта массива, чтобы наполнять данными элемент TextView, размещённый внутри указанной разметки. Если вы используете ArrayAdapter , где в параметре используется ваш собственный класс, а не String, то можно переопределить метод toString() в вашем классе. Пример такого решения есть в конце статьи Android: Простейшая база данных. Часть вторая.
Другой способ. Мы хотим выводить данные не в одно текстовое поле, а в два. Стандартная разметка для списка с одним TextView нам не подойдёт. Придётся самостоятельно создавать нужную разметку и наполнять её данными.
В этому случае нужно наследоваться от класса ArrayAdapter, указывая конкретный тип и переопределяя метод getView(), в котором указать, какие данные должны вставляться в новой разметке.
Метод getView() принимает следующие параметры: позицию элемента, в котором будет выводиться информация, компонент для отображения данных (или null), а также родительский объект ViewGroup, в котором указанный компонент поместится. Вызов метода getItem() вернёт значение из исходного массива по указанному индексу. В результате метод getView() должен вернуть экземпляр компонента, наполненный данными.
Допустим, у нас есть простой класс Cat с двумя полями — имя и пол. Нашему списку понадобится специальная разметка, состоящая из двух текстовых полей. Создадим адаптер, который будет использовать класс Cat вместо String и будем извлекать данные из объекта класса.
Как видите, достаточно просто изменить программу, используя свой класс вместо String.
В методе getView() используется не совсем корректная версия метода inflate(). Подробнее об этом читайте в статье LayoutInflater
Класс ArrayAdapter позволяет динамически изменять данные. Метод add() добавляет в конец массива новое значение. Метод insert() добавляет новое значение в указанную позицию массива. Метод remove() удаляет объект из массива. Метод clear() очистит адаптер. Метод sort() сортирует массив. После него нужно вызвать метод notifyDataSetChanged.
Несколько советов
ArrayAdapter имеет шесть конструкторов.
- ArrayAdapter(Context context, int resource)
- ArrayAdapter(Context context, int resource, int textViewResourceId)
- ArrayAdapter(Context context, int resource, T[] objects)
- ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects)
- ArrayAdapter(Context context, int resource, List objects)
- ArrayAdapter(Context context, int resource, int textViewResourceId, List objects)
У них у всех первые два параметра — это контекст и идентификатор ресурса для разметки. Если корневой элемент разметки является контейнером вместо TextView, то используйте параметр textViewResourceId, чтобы подсказать методу getView(), какой компонент используется для вывода текста.
Сам адаптер работает с данными, как со списками. Если вы используете стандартный массив, то адаптер переконвертирует его в список. Сами данные необязательно сразу передавать адаптеру, можно сделать это позже через метод addAll().
Другие полезные методы адаптера:
- add() — добавляет объект в коллекцию
- remove() — удаляет объект из коллекции
- getItem(int position) — возвращает объект из позиции position
- getContext() — получает контекст
На последний метод следует обратить внимание при создании собственного адаптер на основе ArrayAdapter. Не нужно в своём классе объявлять контекст таким образом.
Через метод getContext() вы уже можете получить доступ к контексту, не объявляя новой переменной.
Тоже самое применимо и к массивам. Не нужно объявлять массив:
Используйте метод getItem(position), который может получить доступ к массиву.
Если позволяет логика, используйте списки вместо массивов для большей гибкости. Тогда вы можете добавлять и удалять данные через методы add() и remove().
Источник
Адаптеры и списки
ListView и ArrayAdapter
Android представляет широкую палитру элементов,которые представляют списки. Все они является наследниками класса android.widget.AdapterView . Это такие виджеты как ListView, GridView, Spinner. Они могут выступать контейнерами для других элементов управления
При работе со списками мы имеем дело с тремя компонентами. Во-первых, это визуальный элемент или виджет, который на экране представляет список (ListView, GridView) и который отображает данные. Во-вторых, это источник данных — массив, объект ArrayList, база данных и т.д., в котором находятся сами отображаемые данные. И в-третьих, это адаптер — специальный компонент, который связывает источник данных с виджетом списка.
Одним из самых простых и распространенных элементов списка является виджет ListView . Рассмотрим связь элемента ListView с источником данных с помощью одного из таких адаптеров — класса ArrayAdapter .
Класс ArrayAdapter представляет собой простейший адаптер, который связывает массив данных с набором элементов TextView , из которых, к примеру, может состоять ListView . То есть в данном случае источником данных выступает массив объектов. ArrayAdapter вызывает у каждого объекта метод toString() для приведения к строковому виду и полученную строку устанавливает в элемент TextView.
Посмотрим на примере. Итак, разметка приложения может выглядеть так:
Здесь также определен элемент ListView, который будет выводить список объектов. Теперь перейдем к коду activity и свяжем ListView через ArrayAdapter с некоторыми данными:
Здесь вначале получаем по id элемент ListView и затем создаем для него адаптер.
Для создания адаптера использовался следующий конструктор ArrayAdapter (this,android.R.layout.simple_list_item_1, countries) , где
this : текущий объект activity
android.R.layout.simple_list_item_1 : файл разметки списка, который фреймворк представляет по умолчанию. Он находится в папке Android SDK по пути platforms/[android-номер_версии]/data/res/layout. Если нас не удовлетворяет стандартная разметка списка, мы можем создать свою и потом в коде изменить id на id нужной нам разметки
countries : массив данных. Здесь необязательно указывать именно массив, это может быть список ArrayList .
В конце неоходимо установить для ListView адаптер с помощью метода setAdapter() .
Источник
Полный список
— создаем свой адаптер на основе BaseAdapter
Предоставляемые нам адаптеры универсальны и полезны, но иногда их возможностей не хватает для реализации задуманного. Тогда возникает необходимость написать свой адаптер. Попробуем и мы. Создавать будем не с нуля, а используя BaseAdapter.
Сделаем подобие интернет магазина. Будем выводить список товаров. Каждый пункт списка будет содержать название товара, цену и изображение. Также будет возможность отметить пункт галкой, поместив его тем самым в корзину.
Внизу списка сделаем кнопку, которая будет отображать содержимое корзины. В настоящем интернет-магазине мы повесили бы на нее, например, переход к созданию заказа.
Project name: P0541_CustomAdapter
Build Target: Android 2.3.3
Application name: CustomAdapter
Package name: ru.startandroid.develop.p0541customadapter
Create Activity: MainActivity
В файл strings.xml добавим текстовый параметр для названия кнопки.
layout для пункта списка – item.xml:
Чекбокс, пара текстовых полей и картинка.
Теперь пишем код. Можно все написать в MainActivity.java, но тогда он получится достаточно большим и неудобным для чтения. Я раскидаю весь код по трем классам.
Product.java – класс, описывающий товар:
Тут все просто – только конструктор и элементы класса. Не заморачиваюсь с доступом и методами Set/Get, чтобы не усложнять код.
BoxAdapter.java – созданный адаптер, который будем использовать
На всякий случай напомню общий принцип действия адаптера: он получает данные и выдает View для отображения пункта списка.
Смотрим код. В конструкторе мы заполняем наши внутренние переменные и получаем LayoutInflater для работы с layout-ресурсами. В objects у нас теперь хранится список товаров, которые надо отобразить в списке.
Методы, отмеченные аннотацией @Override, мы обязаны реализовать при наследовании BaseAdapter. Эти методы используются списком и должны работать корректно.
Метод getCount должен возвращать кол-во элементов. Мы возвращаем кол-во товаров.
Метод getItem должен возвращать элемент по указанной позиции. Используя позицию, получаем конкретный элемент из objects.
Метод getItemId должен возвращать id элемента. Здесь не заморачиваемся и возвращаем позицию. Кстати, также сделано в некоторых адаптерах. Поэтому мы и видели в обработчиках, что >
Метод getView должен возвращать View пункта списка. Для этого мы создавали layout-ресурс R.layout.item. В этом методе мы должны из R.layout.item создать View, заполнить его данными и отдать списку. Но перед тем как создавать, мы пробуем использовать convertView, который идет на вход метода. Это уже созданное ранее View, но неиспользуемое в данный момент. Например, при прокрутке списка, часть пунктов уходит за экран и их уже не надо прорисовывать. View из этих «невидимых» пунктов используются для новых пунктов. Нам остается только заполнить их данными. Это значительно ускоряет работу приложения, т.к. не надо прогонять inflate лишний раз.
Если же convertView в этот раз нам не дали (null), то создаем сами view. Далее заполняем наименования, цену и картинку из данных по товарам. Для чекбокса мы присваиваем обработчик, сохраняем в Tag позицию элемента и ставим галку, если товар уже в корзине.
Tag – это некое Object-хранилище у каждого View, куда вы можете поместить нужные вам данные. В нашем случае я для каждого чекбокса помещаю в его Tag номер позиции пункта списка. Далее в обработчике чекбокса я смогу этот номер позиции извлечь и определить, в каком пункте списка был нажат чекбокс.
В итоге, метод getView возвращает списку полностью заполненное view, и список его отобразит как очередной пункт.
Далее идет пара методов, которые не обязательно было создавать при наследовании BaseAdapter. Я их создал для удобства.
Метод getProduct – это аналог getItem, но он сразу конвертирует Object в Product. Он используется всего пару раз. И в принципе, можно было бы и без него обойтись.
Метод getBox проверяет, какие товары отмечены галками и формирует из них коллекцию-корзину.
myCheckChangeList – обработчик для чекбоксов. Когда мы нажимаем на чекбокс в списке, он срабатывает, читает из Tag позицию пункта списка и помечает соответствующий товар, как положенный в корзину.
Тут важно понимать, что без этого обработчика не работало бы помещение товаров в корзину. Да и на экране — значения чекбоксов в списке терялись бы при прокрутке. Потому что пункты списка пересоздаются, если они уйдут «за экран» и снова появятся. Это пересоздание обеспечивает метод getView, а он для заполнения View берет данные из товаров. Значит при нажатии на чекбокс, обязательно надо сохранить в данных о товаре то, что он теперь в корзине.
Остается накодить MainActivity.java:
Тут кода совсем мало.
В onCreate создаем адаптер и список.
В методе fillData генерируем данные для адаптера. В качестве картинки используем стандартную для всех пунктов. В идеале, для каждого товара своя картинка.
Метод showResult получает из адаптера список товаров корзины и выводит их наименования. Этот метод вызывается по нажатию кнопки на экране, т.к. прописан в ее свойстве onClick.
Все сохраняем и запускаем. Отмечаем товары и жмем кнопку для просмотра содержимого корзины.
Достаточно непростой получился пример из-за чекбокса.
Вполне может быть, что есть другой способ реализации этого примера. Но смысл был в том, чтобы показать создание своего адаптера. Для закрепления темы посмотрите еще этот гугловский пример.
На следующем уроке:
— используем Header и Footer в списках
— разбираемся, как и где используется HeaderViewListAdapter
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник