LiteCoding

Заметки о программировании

Нестандартный ListView своими руками

without comments

Честно говоря, я немного покривил душой, когда придумывал название статьи. Речь сейчас пойдет не столько о нестандартном списке, сколько о нестандартно выглядящих элементах списка. Сам по себе Android API предоставляет очень удобный инструмент для создания списков с нестандартными элементами. Иногда этих возможностей достаточно, тогда с помощью класса SimpleAdapter и несложных манипуляций можно получить требуемый результат (подробности можно посмотреть, например, в одной из статей блога Sai Geetha). Но когда требуется изменить отображение элемента в соответствии с состоянием объекта, приходится садиться за собственную реализацию.

И вот, появляется задача изменить цвет фона элементов на четных позициях, отображать те или иные вложенные элементы в зависимости от флагов и значений. Добро пожаловать в мир настоящих списков с нестандартными элементами.

Опытный читатель сразу же воскликнет: «Подождите, ведь есть SimpleAdapter и SimpleAdapter.ViewBinder, которые делают примерно то же самое», и будет совершенно прав. Ничто не мешает нам поступить следующим образом:

public class MyBinder implements ViewBinder
{
    @Override
    public boolean setViewValue(View view, Object data, String textRepresentation)
    {
        switch(view.getId())
        {
            case R.id.someView1:
            {
                ((TextView)view).setText(textRepresentation);
            }
            case R.id.someView2:
            {
                ((TextView)view).setText(((File)data).getName());
            }
            default:
            {
                break;
            }
        }
    }
}

Однако, у этого метода есть два минуса:
1. Использовать в качестве источника данных карты не всегда удобно.
2. Недостаточная гибкость. Источник данных должен содержать уже обработанные данные для отображения.

Лишь в немногих случаях эти недостатки причиняют какие-то неудобства, но когда такая ситуация возникает, сразу же возникает вопрос поиска эффективного решения. В качестве такого решения я предлагаю вашему вниманию библиотеку android-classkit, которая пока еще мало что умеет, но уже вполне способна решить поставленную задачу.

Для создания нестандартного ListView нам понадобится xml с описанием структуры ячейки списка и совсем немного кода, чтобы заставить все это работать. Если вы заглядывали в исходный код фреймворка, чтобы понять, как именно устроено отображение списка, то вероятно уже знаете, что создание и заполнение элементов списка возложено на плечи адаптера (Adapter). Т.к. стандартные адаптеры нам не подходят, придется написать собственную реализацию или воспользоваться готовой библиотекой android-classkit.

Предположим, что у нас есть следующий вид элемента списка:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:id="@+id/itemLayout">
    <ImageView 
        android:id="@+id/icon" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content" 
        android:src="@drawable/icon" 
        android:layout_alignParentLeft="true" />
    <TextView 
        android:id="@+id/textHeader" 
        android:layout_width="wrap_content" 
        android:text="Header" 
        android:layout_height="wrap_content" 
        android:layout_toRightOf="@+id/icon" 
        android:layout_alignBottom="@+id/icon" />
    <TextView 
        android:id="@+id/textDesc" 
        android:layout_width="wrap_content" 
        android:text="Descriprion" 
        android:layout_height="wrap_content" 
        android:layout_toRightOf="@+id/icon" 
        android:layout_alignTop="@+id/icon" />
    <TextView 
        android:id="@+id/textAttr" 
        android:layout_alignParentRight="true" 
        android:layout_width="wrap_content" 
        android:text="Attrs" 
        android:layout_height="wrap_content" />
</RelativeLayout>

Для того, чтобы заполнить список такими элементами, нам потребуется сделать 2 простых действия:

1. Написать класс, заполняющий элемент списка (он должен реализовывать интерфейс ObjectMapper).
Тут есть один небольшой нюанс. Т.к. элементы списка (точнее, корневые View элементов списка) могут использоваться повторно, то сначала нужно вернуть их в состояние по умолчанию, а затем уже изменять. Т.е. если у большинства элементов цвет фона черный, а у некоторых, в зависимости от состояния привязанного объекта, желтый, то сначала нужно принудительно выставить цвет фона в черный, а затем перекрасить в желтый или не перекрашивать вовсе.

Выглядеть этот класс будет примерно так:

class ListViewExampleMapper implements ObjectMapper
{

	@Override
	public void mapData(int position, View view, ListViewExampleItem object)
	{
		TextView header = (TextView)view.findViewById(R.id.textHeader);
		TextView desc = (TextView)view.findViewById(R.id.textDesc);
		TextView attrs = (TextView)view.findViewById(R.id.textAttr);
		RelativeLayout layout = (RelativeLayout)view.findViewById(R.id.itemLayout);
		
		if(position == 0)
		{
			layout.setBackgroundColor(0xff000000);
		}
		else if(position % 2 == 0)
		{
			layout.setBackgroundColor(0xff808080);
		}
		else
		{
			layout.setBackgroundColor(0xff505050);
		}
		
		if(object.desc.indexOf("cyan") >= 0)
		{
			desc.setTextColor(0xff00ffff);
		}
		else if(object.desc.indexOf("green") >= 0)
		{
			desc.setTextColor(0xff00ff00);
		}
		else
		{
			desc.setTextColor(0xffffffff);
		}
		
		header.setTextColor(0xffffffff);
		attrs.setTextColor(0xffffffff);
		
		header.setText(object.header);
		desc.setText(object.desc);
		attrs.setText(object.attrs);
	}
	
}

2. Создать адаптер, который будет пользоваться нашим классом из п.1, и назначить его списку.

ObjectAdapter<ListViewExampleItem> adapter = new ObjectAdapter<ListViewExampleItem>(this.getLayoutInflater(), itemList, R.layout.listview_example_item, new ListViewExampleMapper());
        listView.setAdapter(adapter);

R.layout.listview_example_item в данном случае — это layout id нашего xml с элементом списка. Все остальное сделают ListView и ObjectAdapter.

Пример использования можно посмотреть в репозитории (проект android-classkit-test).
Скачать android-classkit.

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • LinkedIn
  • Tumblr

Written by Дмитрий Воробьев

Суббота, Январь 14th, 2012 at 15:11

Leave a Reply

You must be logged in to post a comment.