Освой программирование играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

ArrayAdapter

Создание адаптера
Используем ресурсы
Динамическое наполнение
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

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

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;
		}
	}
}

ArrayAdapter

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

В методе getView() используется не совсем корректная версия метода inflate(). Подробнее об этом читайте в статье LayoutInflater

Класс ArrayAdapter позволяет динамически изменять данные. Метод add() добавляет в конец массива новое значение. Метод insert() добавляет новое значение в указанную позицию массива. Метод remove() удаляет объект из массива. Метод clear() очистит адаптер. Метод sort() сортирует массив. После него нужно вызвать метод notifyDataSetChanged.


// добавляем нового кота в список
Cat ginger = new Cat("Рыжик", "кошак");
adapter.add(ginger);
adapter.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. Не нужно в своём классе объявлять контекст таким образом.


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().

Дополнительное чтение

Создание собственного адаптера для списка задач

Реклама