Освой программирование играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Создание адаптера
Используем ресурсы
Динамическое наполнение
ListAdapter
SpinnerAdapter
Переопределяем адаптер
ArrayAdapter является простейшим адаптером, который специально предназначен для работы с элементами списка типа ListView, Spinner, GridView и им подобным. Создать адаптер этого вида можно так:
// используется системная разметка
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_list_item1, new string[]{"Рыжик", "Барсик", "Мурзик"});
В параметрах используется контекст, XML-разметка для отдельного элемента списка и массив данных. Контекстом может быть сама активность (this), под разметкой подразумевается компонент, в котором выводится текст, например, TextView, а данными является подготовленный массив, все элементы которого по очереди вставляются в указанную разметку.
Разметку можно создать самостоятельно, а можно использовать готовую системную разметку. Если посмотреть на исходники файла simple_list_item_1.xml в документации Android SDK, то увидим, что он содержит TextView. В этом коде мы создали адаптер ArrayAdapter, в котором данные элемента TextView представлены в виде строк.
Чтобы код был более читаемым, можно сделать ещё так:
// определяем массив типа String
final String[] catNames = new String[] {
"Рыжик", "Барсик", "Мурзик"
};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, catNames);
listView.setAdapter(adapter);
Мы вынесли массив строк в отдельную переменную.
Если у нас есть готовый файл ресурсов (массив строк), то можно использовать специальный метод createFromResource(), который может создать ArrayAdapter из ресурсов:
Подготовим массив строк:
<string-array name="dayofweek">
<item>Понедельник</item>
<item>Вторник</item>
<item>Среда</item>
<item>Четверг</item>
<item>Котопятница</item>
<item>Субкота</item>
<item>Воскресенье</item>
</string-array>
Теперь мы можем воспользоваться адаптером и применить к Spinner:
Spinner spinner = (Spinner) findViewById(R.id.spinner1);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.dayofweek, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
В этом примере мы использовали системную разметку android.R.layout.simple_spinner_item, которая тоже содержит TextView.
При использовании этого метода вы можете применять локализованные версии, что очень удобно.
Или можно пойти другим путём. Получить массив из ресурсов и вставить его в адаптер, как в самом первом примере.
Spinner spinner = (Spinner) findViewById(R.id.spinner);
final String[] daynames = getResources().getStringArray(
R.array.dayofweek);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, daynames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
Также мы можем создать массив программно.
Spinner spinner = (Spinner) findViewById(R.id.spinner);
List<String> catNames = new ArrayList<String>();
catNames.add("Барсик");
catNames.add("Мурзик");
catNames.add("Рыжик");
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, catNames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
ListAdapter является интерфейсом. По сути ничего не меняется. Заменяем ArrayAdapter<String> adapter на ListAdapter adapter и получаем тот же результат.
ListView lvDayOfWeek = (ListView)findViewById(R.id.listView);
final String[] dayNames = getResources().getStringArray(
R.array.dayofweek); // массив строк мы определили в ресурсах ранее
ListAdapter adapter = new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1, dayNames);
lvDayOfWeek.setAdapter(adapter);
Данный интерфейс может пригодиться при создании собственного адаптера, а для стандартных случаев выгоды не вижу.
SpinnerAdapter также является интерфейсом и может использоваться при создании собственных адаптеров на основе ArrayAdapter. В стандартных ситуациях смысла использования его нет. Вот так будет выглядеть код:
final String[] dayNames = getResources().getStringArray(
R.array.dayofweek); // массив строк мы определили в ресурсах ранее
Spinner spinner = (Spinner) findViewById(R.id.spinner);
SpinnerAdapter adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, dayNames);
((ArrayAdapter<String>) adapter).setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
По умолчанию ArrayAdapter использует метод toString() из объекта массива, чтобы наполнять данными элемент TextView, размещённый внутри указанной разметки. Если вы используете ArrayAdapter<T>, где в параметре <T> используется ваш собственный класс, а не String, то можно переопределить метод toString() в вашем классе. Пример такого решения есть в конце статьи Android: Простейшая база данных. Часть вторая.
Другой способ. Мы хотим выводить данные не в одно текстовое поле, а в два. Стандартная разметка для списка с одним TextView нам не подойдёт. Придётся самостоятельно создавать нужную разметку и наполнять её данными.
В этому случае нужно наследоваться от класса ArrayAdapter, указывая конкретный тип и переопределяя метод getView(), в котором указать, какие данные должны вставляться в новой разметке.
Метод getView() принимает следующие параметры: позицию элемента, в котором будет выводиться информация, компонент для отображения данных (или null), а также родительский объект ViewGroup, в котором указанный компонент поместится. Вызов метода getItem() вернёт значение из исходного массива по указанному индексу. В результате метод getView() должен вернуть экземпляр компонента, наполненный данными.
Допустим, у нас есть простой класс Cat с двумя полями - имя и пол. Нашему списку понадобится специальная разметка, состоящая из двух текстовых полей. Создадим адаптер, который будет использовать класс Cat вместо String и будем извлекать данные из объекта класса.
package ru.alexanderklimov.test;
import ...
public class MainActivity extends ListActivity {
private static final List<Cat> cats = new ArrayList<Cat>();
static {
cats.add(new Cat("Васька", "котэ"));
cats.add(new Cat("Мурзик", "котяра"));
cats.add(new Cat("Мурка", "кошка"));
cats.add(new Cat("Барсик", "котик"));
cats.add(new Cat("Лиза", "кошечка"));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayAdapter<Cat> adapter = new CatAdapter(this);
setListAdapter(adapter);
}
private static class Cat {
public final String name;
public final String gender;
public Cat(String name, String gender) {
this.name = name;
this.gender = gender;
}
}
private class CatAdapter extends ArrayAdapter<Cat> {
public CatAdapter(Context context) {
super(context, android.R.layout.simple_list_item_2, cats);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Cat cat = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(android.R.layout.simple_list_item_2, null);
}
((TextView) convertView.findViewById(android.R.id.text1))
.setText(cat.name);
((TextView) convertView.findViewById(android.R.id.text2))
.setText(cat.gender);
return convertView;
}
}
}
Как видите, достаточно просто изменить программу, используя свой класс вместо String.
В методе getView() используется не совсем корректная версия метода inflate(). Подробнее об этом читайте в статье LayoutInflater
Класс ArrayAdapter позволяет динамически изменять данные. Метод add() добавляет в конец массива новое значение. Метод insert() добавляет новое значение в указанную позицию массива. Метод remove() удаляет объект из массива. Метод clear() очистит адаптер. Метод sort() сортирует массив. После него нужно вызвать метод notifyDataSetChanged.
// добавляем нового кота в список
Cat ginger = new Cat("Рыжик", "кошак");
adapter.add(ginger);
adapter.notifyDataSetChanged();
ArrayAdapter имеет шесть конструкторов.
У них у всех первые два параметра - это контекст и идентификатор ресурса для разметки. Если корневой элемент разметки является контейнером вместо TextView, то используйте параметр textViewResourceId, чтобы подсказать методу getView(), какой компонент используется для вывода текста.
Сам адаптер работает с данными, как со списками. Если вы используете стандартный массив, то адаптер переконвертирует его в список. Сами данные необязательно сразу передавать адаптеру, можно сделать это позже через метод addAll().
Другие полезные методы адаптера:
На последний метод следует обратить внимание при создании собственного адаптер на основе ArrayAdapter. Не нужно в своём классе объявлять контекст таким образом.
public class CustomArrayAdapter extends ArrayAdapter{
private Context mContext;
// где-то в конструкторе
mContext = context;
}
Через метод getContext() вы уже можете получить доступ к контексту, не объявляя новой переменной.
Тоже самое применимо и к массивам. Не нужно объявлять массив:
private Cat[] mCats;
// где-то в конструкторе
CustomArrayAdapter(Context context, int resource, Cats[] cats){
mCats = cats;
...
}
Используйте метод getItem(position), который может получить доступ к массиву.
Если позволяет логика, используйте списки вместо массивов для большей гибкости. Тогда вы можете добавлять и удалять данные через методы add() и remove().
Создание собственного адаптера для списка задач