Андроид – создание Custom ListView

Рано или поздно приходит момент, когда набор стандартных виджетов становится тесен. Хочется добавить к базовым возможностям контролов дополнительные опции, способные обогатить интерфейс и улучшить пресловутый 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

Leave a Reply

Your email address will not be published.