Рано или поздно приходит момент, когда набор стандартных виджетов становится тесен. Хочется добавить к базовым возможностям контролов дополнительные опции, способные обогатить интерфейс и улучшить пресловутый UX(User experience). Все что необходимо для расширения функциональности виджетов – немного покопаться в документации.
В данном посте описываются основы создания вашего собственного расширенного ListView. Следуя описанному здесь приему вы легко сможете добавлять к определенным строкам картинки, создавать сложные лайауты и отдельные обработчики событий для разных элементов ListView.
Ключевых момента тут два – создание собственного адаптера и использование его в активити, расширяющей обычную ListActivity.
I Этап
1. На основе ArrayAdapter создаем свой собственный объект – My_Custom_Adapter – он будет хранить все возможные типы строк в нашем ListView (например строка с текстом и строка, где кроме текста есть какая-нибудь иконка).
2. Для хранения всех элементов – all_items в сниппете – можно использовать любой класс-контейнер (в зависимости от ваших целей) – например ArrayList. Продумайте какой-тип объектов лучше подходит для ваших целей. В нашем примере – достаточно использовать обычные строки для заголовков. Для простоты определения типа элементов ArrayList создадим TreeSet, в который будем заносить индексы элементов второго типа.
3. Переопределяем для него функцию getView() – которая в зависимости от типа строчки возвращает view, соответствующее типу строки.
4. Переопределяем функцию getItemViewType() – она возвращает тип элемента по его индексу (а все индексы элементов второго типа мы храним в TreeSet’e).
5. Определим функции для добавления строк в наш ListView обоих типов – add_Item_Type_1(), add_Item_Type_2() – в любом случае, элемент добавляется в общий массив ArrayList, но если мы добавляем элемент второго типа – его индекс заносится в контейнер типа TreeSet. Важно после любого изменения в ArrayList вызывать метод notifyDataSetChanged() – для обновления картинки на экране телефона.
6. Набор методов для обновления адаптера при изменении информации (например при пересчете каких характеристик, обновлении данных с сервера и т.п.) – удобно объединить в одну функцию – Clear_Data().
Шаблонный сниппет для создания адаптера для CustomListView.
private class My_Custom_Adapter extends ArrayAdapter { private LayoutInflater my_Inflater; private static final int TYPE_1 = 0; private static final int TYPE_2 = 1; private TreeSet Second_Items_Set = new TreeSet(); private ArrayList My_Custom_Object all_items; public My_Custom_Adapter(Context context, int textViewResourceId, ArrayList My_Custom_Object items) { super(context, textViewResourceId, items); mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.all_items = items; } public View getView(int position, View convertView, ViewGroup parent) { int type = getItemViewType(position); View v = convertView; switch (type) { case TYPE_1: v = mInflater.inflate(R.layout.base_row_for_first_type, null); break; case TYPE_2: v = mInflater.inflate(R.layout.base_row_for_second_type, null); break; default: // todo: deal with this situation break; } My_Custom_Object cur_element = all_items.get(position); switch (type) { case TYPE_1: if (cur_element != null) { // TODO: According to object's data // fill layout with appropriate data: // specified picture, text, etc } break; case TYPE_2: if (cur_element != null) { // TODO: According to object's data // fill layout with appropriate data: // specified picture, text, etc } break; default: // todo: deal with this situation break; } return v; } public void add_Item_Type_1(final String item_name) { item_type_1_list.add(item_name); notifyDataSetChanged(); } public void add_Item_Type_2(final String item_name) { item_type_2_list.add(item_name); Second_Items_Set.add(all_items.size()-1); notifyDataSetChanged(); } @Override public int getItemViewType(int position) { return Second_Items_Set.contains(position) ? TYPE_2 : TYPE_1; } public void Clear_Data() { Second_Items_Set.clear(); all_items.clear(); clear(); } }
II Этап
Cоздаем лайауты для различных типов строк, а также соответствующим образом их инициируем данными в методе getView() нашего адаптера.
Ссылки на функции, которые должны вызываться при нажатии – можно прописать прямо в xml-файлах разметки, заполнив поле onClick (в сниппете это – put_name_of_your_function_here), установив значение clickable в true, или в коде – как вам удобней.
<!--?xml version="1.0" encoding="utf-8"?--> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/custom_row_item" android:layout_width="fill_parent" android:layout_height="fill_parent" android:clickable="true" android:onClick="put_name_of_your_function_here" > <!-- PUT Your Markup here --> </LinearLayout>
III Этап
Для лайаута самой активити используем типовый лайаут для ListView:
<!--?xml version="1.0" encoding="utf-8"?--> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/custom_list_view" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="fill_parent" > <ListView android:id="@id/android:list" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>
На основе ListActivity создаем свою собственную активити, в которую добавляем созданным нами адаптер.
Создаем метод который будет устанавливать адаптер для нашей активити – Prepare_Adapter(), а также метод Update_Activity_By_Some_Rule() который будет обновлять данные в нем по некоторым вашим правилам – например подгружая соответствующие строчки для очередной записи, отображаемой в ListView.
public class My_List_Activity extends ListActivity { private ArrayList all_objects = null; private My_Custom_Adapter Cur_Adapter; @Override public void onCreate(Bundle savedInstanceState) { setContentView(R.layout.custom_list_view); super.onCreate(savedInstanceState); Prepare_Adapter(); } private void Prepare_Adapter() { if (Cur_Adapter == null) { this.Cur_Adapter = new My_Custom_Adapter(this, R.layout.base_row_for_first_type, all_objects); } else { Cur_Adapter.Clear_Data(); } setListAdapter(this.Cur_Adapter); } private void Update_Activity_By_Some_Rule() { Cur_Adapter.add_Item_Type_1("Test item 1"); Cur_Adapter.add_Item_Type_2("Test item 2"); } }
Android и Custom ListView еще ссылки на эту тему:
http://www.softwarepassion.com/android-series-custom-listview-items-and-adapters/
http://www.pocketmagic.net/?p=1678
http://www.androidsnippets.com/clickable-listview-items