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().
Источник
SimpleAdapter
Конструктор класса SimpleAdapter имеет следующий вид:
Для данных в параметре data используется коллекция Map-объектов или его наследников, например, HashMap. Каждый Map содержит данные для отдельного элемента списка. Чтобы адаптер понимал, какие данные нужно вставлять в View-компоненты каждого пункта списка, мы указываем два массива from и to. В массиве from используем ключи из Map, а в массиве to – идентификаторы компонентов. Адаптер последовательно перебирает все компоненты из массива to и сопоставляет им соответствующие значения из from. Следите, чтобы в массиве to было не больше элементов, чем в from, иначе программа вызовет ошибку.
Используем системную разметку
При работе с адаптером ArrayAdapter для ListView мы использовали готовую разметку android.R.layout.simple_list_item_1, которая состоит из одного TextView. Существует другая системная разметка android.R.layout.simple_list_item_2, состоящая из двух текстовых меток с идентификаторами android.R.id.text1 и android.R.id.text2, расположенных в двух строчках. В первой метке выводится текст большим шрифтом, а во втором — маленьким.
При желании можно было наследоваться от ArrayAdapter и реализовать работу с такой разметкой. Но SimpleAdapter уже оптимизирован для работы с таким случаем, поэтому мы можем сразу использовать его.
Напишем пример, где у каждого кота будет его имя и телефон.
Мы создали два ключа Name и Tel и программно заполняем их именами котов и их телефонами. Все телефоны вымышлены, не надо звонить по ним! Коты не дают свои номера кому попало.
Используем собственную разметку
Допустим, мы хотим вывести список котов в три колонки: номер, имя кота и его телефон (Ух, ты).
Создадим отдельную разметку для каждого элемента списка:
Код для связывания адаптера со списком:
Результат будет следующим:
Изменения минимальны. Мы для каждого кота добавили порядковый номер MemberID, а в адаптере указали не системную, а собственную разметку R.layout.list_item. А также используем массив из трёх элементов.
ListAdapter
В приведённых примерах вы можете безбоязненно заменить SimpleAdapter на ListAdapter:
Это на тот случай, чтобы вы не пугались, если встретите такой код.
Сложная разметка
Разметка может быть любой и включать в себя не только текстовые метки, но и другие компоненты, такие как ImageView и CheckBox. Принцип остаётся такой же. Создаём новую разметку и связываем данные с каждым компонентом.
Создадим новую разметку (или отредактируем файл list_item.xml из предыдущего примера)
Смотрим, какие тут отличия. Теперь мы используем не только строки для имён котов, но и булево значение для флажка и целое число для значка. Поэтому, выражение HashMap заменим на более универсальный HashMap
Смотрим на результат. Я знаю только одного кота, который слушает да ест. Поэтому пометим его как накормленного по умолчанию.
Мы создали два массива, которые будут использованы для сопоставления данных и компонентов. Массив from содержит имена Map-ключей, а массив to – идентификаторы компонентов. Таким образом, в первый TextView с идентификатором R.id.textview_name будет вставлено первое значение ключа CatName и так далее. Аналогично происходит и с другими компонентами CheckBox и ImageView.
Адаптер перебирает все компоненты из массива to для каждого элемента списка и сопоставляет им значения из массива from. Получается такая таблица:
R.id.R.id.textview_name – Map.get(«CatName»)
R.id.checkbox_feed — Map.get(«IsHungry»)
R.id.imageview_cat — Map.get(«Icon»)
Тут возникает интересный вопрос, а как адаптер понимает, какой именно метод нужно вызвать для компонента, чтобы передать ему значение. К примеру, для компонента CheckBox нам нужно установить установить флажок или снять его, а не выводить текст, как для TextView.
Тут о нас уже позаботились. Адаптер смотрит, с чем имеет дело и в зависимости от типа компонента принимает решение. Вариантов немного, всего три.
Первый вариант — если компонент является TextView или его наследником, то вставляется текст. Иными словами, вызывается метод SimpleAdapter.setViewText(), который вызывает TextView.setText() и передает туда нужное значение из Map.
Второй вариант — если компонент, наследует интерфейс Checkable, то адаптер проверяет, является ли соответствующее значение из Map типа boolean. Если это действительно так, то вызывается метод Checkable.setChecked. Если значение не является типом boolean, то проверяется, является ли компонент наследником TextView. Если ответ положительный, то тогда вставляется текстовое значение из Map. В других случаях мы получим ошибку. К компонентам с интерфейсом Checkable относятся CheckBox, CheckedTextView, CompoundButton, RadioButton, Switch, ToggleButton. Вы можете сами в этом убедиться. Заменим блок CheckBox на TextView в разметке list_item.xml:
Запустим проект и увидим следующее:
К счастью, приложение не завершается с ошибкой, а просто выводит значения булевой переменной.
Третий вариант — если компонент является ImageView или его наследником, то проверяется тип данных. Если тип можно привести к типу Integer, то вызывается метод SimpleAdapter.setViewImage(ImageView v, int value), который вызывает метод ImageView.setImageResource(), т.е. подставляется идентификатор ресурса из папки drawable или mipmap. Если тип другой, то вызывается метод SimpleAdapter.setViewImage (ImageView v, String value), который пытается привести значение к int и вызвать метод ImageView.setImageResource(). Если такой вариант не проходит, то преобразует строку в объект Uri и вызывает метод ImageView.setImageURI(Uri).
Если компонент не подходит ни под один из трёх вышеперечисленных типов, то мы получим ошибку.
Продолжим опыты. Допустим, мы хотим не только установить флажок у CheckBox, но и новый текст. Так как мы уже знаем, что наследники TextView могут выводить текст, то поступим следующим образом. В наши массивы from и to добавим новые элементы:
Источник
Адаптеры и списки
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() .
Источник