Урок 50. SimpleAdapter. Используем ViewBinder


В этом уроке:

- используем свой SimpleAdapter.ViewBinder


Адаптер SimpleAdapter при своей работе сопоставляет View-компоненты и значения из Map-объектов. Как он это делает по умолчанию и с каким View-компонентами умеет работать, мы рассмотрели в предыдущих уроках. Но если нам не хватает этих возможностей, мы всегда можем создать свой обработчик и присвоить его адаптеру.

Для этого используется метод setViewBinder (SimpleAdapter.ViewBinder viewBinder), который на вход требует объект SimpleAdapter.ViewBinder. Мы создаем свой вариант этого биндера и реализуем в нем метод setViewValue(View view, Object data, String textRepresentation), в котором прописываем всю логику сопоставления данных и компонентов (биндинга). Метод возвращает значение boolean.


Алгоритм работы адаптера таков: он сначала проверяет, давали ли ему сторонний биндер.

Если находит, то выполняет его метод setViewValue. Если метод возвращает true, то адаптер считает, что обработка успешно завершена, если же false – то он выполняет биндинг в своем стандартном алгоритме, который мы рассмотрели на предыдущих уроках.

Если адаптер не находит сторонний биндер, он также выполняет стандартный биндинг.

Т.е. наша задача – заполнить метод SimpleAdapter.ViewBinder.setViewValue. И здесь уже нет ограничений на TextView или ImageView,  может быть обработан и заполнен любой компонент. Создадим пример, в котором будем заполнять значение ProgressBar и менять цвет LinearLayout.

Это будет приложение-мониторинг, которое отображает уровень загрузки мощностей какой-то системы в разрезе дней. ProgressBar будет показывать уровень загрузки, а весь пункт списка будет подкрашиваться цветом в зависимости от уровня загрузки.

Создадим проект:

Project name: P0501_SimpleAdapterCustom2
Build Target: Android 2.3.3
Application name: SimpleAdapterCustom2
Package name: ru.startandroid.develop.p0501simpleadaptercustom2
Create Activity: MainActivity


Рисуем экран main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="@+id/lvSimple"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>

Только список.


Нам понадобится создать файл res/values/colors.xml, где мы перечислим требуемые нам цвета:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="Bckgr">#9C9C9C</color>
<color name="Red">#33FF0000</color>
<color name="Orange">#33FFFF00</color>
<color name="Green">#3300FF00</color>
<color name="Black">#000000</color>
</resources>



Теперь создаем layout для пунктов списка res/layout/item.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/Bckgr">
<LinearLayout
android:id="@+id/llLoad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tvLoad"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:layout_gravity="center_horizontal"
android:textColor="@color/Black">
</TextView>
<ProgressBar
android:id="@+id/pbLoad"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:max="100">
</ProgressBar>
</LinearLayout>
</LinearLayout>

llLoad – LinearLayout который занимает весь пункт списка, и который мы будем разукрашивать,
tvLoad – текст-инфа,
pbLoad – индикатор загрузки.


Код MainActivity.java:

package ru.startandroid.develop.p0501simpleadaptercustom2;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SimpleAdapter;

public class MainActivity extends Activity {

 
// имена атрибутов для Map
 
final String ATTRIBUTE_NAME_TEXT = "text";
 
final String ATTRIBUTE_NAME_PB = "pb";
 
final String ATTRIBUTE_NAME_LL = "ll";

  ListView lvSimple;

 
/** Called when the activity is first created. */
 
public void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);
    setContentView
(R.layout.main);

   
// массив данных
   
int load[] = { 41, 48, 22, 35, 30, 67, 51, 88 };

   
// упаковываем данные в понятную для адаптера структуру
   
ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>(
       
load.length);
    Map<String, Object> m;
   
for (int i = 0; i < load.length; i++) {
     
m = new HashMap<String, Object>();
      m.put
(ATTRIBUTE_NAME_TEXT, "Day " + (i+1) + ". Load: " + load[i] + "%");
      m.put
(ATTRIBUTE_NAME_PB, load[i]);
      m.put
(ATTRIBUTE_NAME_LL, load[i]);
      data.add
(m);
   
}

   
// массив имен атрибутов, из которых будут читаться данные
   
String[] from = { ATTRIBUTE_NAME_TEXT, ATTRIBUTE_NAME_PB,
        ATTRIBUTE_NAME_LL
};
   
// массив ID View-компонентов, в которые будут вставлять данные
   
int[] to = { R.id.tvLoad, R.id.pbLoad, R.id.llLoad };

   
// создаем адаптер
   
SimpleAdapter sAdapter = new SimpleAdapter(this, data, R.layout.item,
        from, to
);
   
// Указываем адаптеру свой биндер
   
sAdapter.setViewBinder(new MyViewBinder());

   
// определяем список и присваиваем ему адаптер
   
lvSimple = (ListView) findViewById(R.id.lvSimple);
    lvSimple.setAdapter
(sAdapter);
 
}

 
class MyViewBinder implements SimpleAdapter.ViewBinder {
   
   
int red = getResources().getColor(R.color.Red);
   
int orange = getResources().getColor(R.color.Orange);
   
int green = getResources().getColor(R.color.Green);
   
   
@Override
   
public boolean setViewValue(View view, Object data,
        String textRepresentation
) {
     
int i = 0;
     
switch (view.getId()) {
     
// LinearLayout
     
case R.id.llLoad:
        i =
((Integer) data).intValue();
       
if (i < 40) view.setBackgroundColor(green); else
          if
(i < 70) view.setBackgroundColor(orange); else
       
view.setBackgroundColor(red);
       
return true;
     
// ProgressBar 
     
case R.id.pbLoad:
        i =
((Integer) data).intValue();
       
((ProgressBar)view).setProgress(i);
       
return true;
     
}
     
return false;
   
}
  }
}

Смотрим код. Заполняем массив данных загрузки по 100-бальной шкале. Формируем данные для адаптера: в TextView будем передавать краткую информацию (String), а в PgrogressBar и LinearLayout – значение загрузки (int). Заполняем массивы сопоставления, создаем адаптер, говорим ему, чтобы использовал наш биндер, и настраиваем список.


Вложенный класс MyViewBinder – это наша реализация биндера. Мы должны реализовать метод setViewValue, который будет использоваться адаптером для сопоставления данных из Map и View-компонентов. На вход ему идут:

view - View-компонент
data - данные для него
textRepresentation - текстовое представление данных (data.toString() или пустой String, но никогда не null)

Итак, смотрим реализацию. Сначала проверяется какой компонент нам дали на обработку.

В случае llLoad мы ожидаем данные типа int, поэтому выполняем приведение Object к Integer и получаем данные по загрузке из массива. А далее смотрим уровень этой загрузки. Если меньше 40, то будем считать, что все ок, цвет фона зеленый. От 40 до 70 – внимание, цвет желтый. Выше 70 – высокая нагрузка, красный цвет. Возвращаем true. Это важно! Тем самым мы говорим адаптеру, что мы успешно выполнили биндинг для данного компонента и выполнять для него стандартную обработку не надо.

В случае pbLoad мы также ожидаем int и выполняем приведение. А затем просто вызываем метод setProgress и передаем ему значение. В конце возвращаем true – мы сами успешно обработали компонент, стандартная обработка не нужна.

Для всех других View-компонентов метод setViewValue будет возвращать false. Это значит, компоненты пойдут на стандартную обработку адаптером. В нашем случае по этому пути отправится tvLoad и адаптер сам передаст ему текст из Map, наше вмешательство не нужно.

Все сохраняем и запускаем.


Получаем вот такую картинку. LinearLayout заполнился цветом, ProgressBar отобразил уровень загрузки, TextView показал информацию о дне и загрузке.


На следующем уроке:

- используем SimpleAdapter для построения списка
- добавляем и удаляем записи в списке