Создаем свой компонент на основе ListView в Android

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

Данный урок создан по просьбе одного из читателей блога. В данном уроке мы создадим свой компонент для TODO приложения в котором будет список задач. Как выглядит один элемент списка можно увидеть ниже.

 

Шаг 1. Прототипирование

Создаем прототип. Выглядеть наш кастомный компонент будет следующим образом:

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

 

Шаг 2. Первоначальная подготовка

Создаем новый проект в Android Studion:

В конце этого урока у нас получится следующая структура проекта:

Теперь давайте посмотрим какой нам предстоит создать компонент:

 

Шаг 3. Создание главного Activity

Создаем MyListView — это главный, который будет первым стартовать при запуске приложения:

package com.devcolibri.app;

import android.app.Activity;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

import com.devcolibri.app.enums.IMGEnum;

import java.util.ArrayList;
import java.util.GregorianCalendar;

public class MyListView extends Activity {

    private ListView listView;
    private MyLazyAdapter adapter;
    private TypedArray images;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Инициализируем изображения с помощью ресурса изображений
        // данный ресурс будет рассмотрен ниже
        Resources res = getResources();
        images = res.obtainTypedArray(R.array.images);

        listView = (ListView) findViewById(R.id.list);

        // инициализация нашего адаптера
        adapter = new MyLazyAdapter(this, initData());
        listView.setAdapter(adapter);

        // По клику будем выводить текст элемента
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(getApplicationContext(), adapter.getItem(position).toString(), 
                                                                          Toast.LENGTH_SHORT).show();
            }
        });
    }

    // Этот медот будет инициализировать список даных для ListView
    private ArrayList<ObjectItem> initData() {
        // ObjectItem это наш POJO объект который мы ниже разберем.
        // Даный список будет возвращаться для заполнения LIstView
        ArrayList<ObjectItem> maps = new ArrayList<ObjectItem>();

        ObjectItem objectItem1 = new ObjectItem("Test 1",
                GregorianCalendar.getInstance().getTime(),
                images.getDrawable(IMGEnum.DONE.index()));

        maps.add(objectItem1);

        ObjectItem objectItem2 = new ObjectItem("Test 2",
                GregorianCalendar.getInstance().getTime(),
                images.getDrawable(IMGEnum.NOTDONE.index()));

        maps.add(objectItem2);

        ObjectItem objectItem3 = new ObjectItem("Test 3",
                GregorianCalendar.getInstance().getTime(),
                images.getDrawable(IMGEnum.DONE.index()));

        maps.add(objectItem3);

        ObjectItem objectItem4 = new ObjectItem("Test 4",
                GregorianCalendar.getInstance().getTime(),
                images.getDrawable(IMGEnum.DONE.index()));

        maps.add(objectItem4);

        return maps;
    }
}

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

А для того, чтобы динамически подставлять изображения, я создал для них отдельный ресурс файл. Ниже посмотрим как его сделать.

Наш Activity нужно описать в AndroidManifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.devcolibri.app"
    android:versionCode="1"
    android:versionName="1.0">

    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" />

    <application android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/AppTheme">

        <activity android:name=".MyListView" android:icon="@drawable/ic_launcher">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

Ну тут все стандартно и понятно, если вы еще не знакомы с тегами этого файла то рекомедую почитать AndroidManifest.xml — для чего он?

И теперь создаем layout назовем его 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/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:listSelector="@drawable/item_list_selector" />

</LinearLayout>

Как вы видите тут мы используем простой ListView, и указали для него selector который даст эефекты нашему списку на определёных состояниях, когда на нем фокус, например.

Пока никакой кастомизации мы не делали, так как все будет завязанно на Адаптере. Это очень удомно, так как вы сможите сменить в случае необходимости Свой адаптер на другой и при этом не нарушив логику приложения.

 

Шаг 4. Создание POJO и Enum

Теперь создадим обьект который будет хранить содержимое каждого элемента списка ListView. Создаем класс и называем его ObjectItem:

package com.devcolibri.app;

import android.graphics.drawable.Drawable;

import java.util.Date;

public class ObjectItem {

    private String title;
    private Date date;
    private Drawable image;

    public ObjectItem(String title, Date date, Drawable image) {
        this.title = title;
        this.date = date;
        this.image = image;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public Drawable getImage() {
        return image;
    }

    public void setImage(Drawable image) {
        this.image = image;
    }
}

Теперь добавьте в drawable следующие изображения:

   

Теперь создаем Enum который будет определять какой тип картинки показывать, назовем его IMGEnum:

package com.devcolibri.app.enums;

public enum IMGEnum {

    NOTDONE(0),
    DONE(1);

    private int id;

    IMGEnum(int index) {
        id = index;
    }

    public int index() {
        return id;
    }
}

И создаем в values рксурс с именем images_arrays.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string-array name="images">
        <item>@drawable/done</item>
        <item>@drawable/close</item>
    </string-array>

</resources>

Этот русурс хранит ссылки на изображения, с помощью этого ресурса и Enum мы сможем с легкостью сделать динамическую подгрузку изображений.

Например, мы можем это сделать так:

images.getDrawable(IMGEnum.NOTDONE.index()));

 

Шаг 5. Создание кастомного элемента ListView

Как вы уже заметили в MyListView мы устанавливаем для нашего списка адаптер, именно этот адаптер будет кастомизировать наш элемент.

Для адаптера создадим view, который мы будем заполнять данными. Проставлять дату, устанавливать картинку и текст.

Содадим layout и назовем его my_listview.xml со следующим содержимым:

<?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="wrap_content"
    android:background="@drawable/item_list_selector"
    android:orientation="horizontal"
    android:padding="5dip" >

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView"
        android:minHeight="60dp"
        android:maxHeight="60dp"
        android:minWidth="60dp"
        android:maxWidth="60dp" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Нарисовать новый логотип для маркетинговой кампании ..."
        android:textSize="18dp"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@+id/imageView"
        android:paddingLeft="10dp"
        android:typeface="sans"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="12:45"
        android:id="@+id/time"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:textSize="10dp" />

</RelativeLayout>

Это вид одного элемента нашего списка, выглядеть он будет так:

Теперь создаем MyLazyAdapter, который будет заполнять список такими элементами как показанно высше:

package com.devcolibri.app;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class MyLazyAdapter extends ArrayAdapter<String> {

    private List<ObjectItem> data;
    private Context context;

    public MyLazyAdapter(Context context, List<ObjectItem> data) {
        super(context, R.layout.my_listview);
        this.data = data;
        this.context = context;
    }

    @Override
    public int getCount() {
        // возвращаем количество элементов списка
        return data.size();
    }

    @Override
    public String getItem(int position) {
        // получение одного элемента по индексу
        return data.get(position).getTitle();
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    // заполнение элементов списка
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        // задаем вид элемента списка, который мы создали высше
        View view = inflater.inflate(R.layout.my_listview, parent, false);

        // проставляем данные для элементов
        TextView title = (TextView)view.findViewById(R.id.title);
        TextView time = (TextView)view.findViewById(R.id.time);
        ImageView thumbImage = (ImageView)view.findViewById(R.id.imageView);

        // получаем элемент со списка
        ObjectItem objectItem = data.get(position);

        // устанавливаем значения компонентам одного эелемента списка
        title.setText(objectItem.getTitle());
        time.setText(objectItem.getDate().toString());
        thumbImage.setImageDrawable(objectItem.getImage());

        return view;
    }
}

 

Шаг 6. Создание стиля для элемента

Для начало создадим зелёную рамку для элемента, создадим drawable-hdpi\item_bg.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#FFF" />
    <stroke android:width="3dip" android:color="#ff16b393" />
</shape>

После этого создаем drawable-hdpi\item_bg_hover.xml это стиль элемента в момент нажатия по нему:

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
    <solid android:color="#ff16b393" />
</shape>

А теперь создадим для этого всего selector, который будет управлять состоянием и видом элемента. Создаем файл drawable-hdpi\item_list_selector.xml:

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Когда по элементу списка нажали применять item_bg_hover.xml -->
    <item android:drawable="@drawable/item_bg_hover"
        android:state_pressed="true" />

    <!-- Когда стоит фокус на элементе списка то применить item_bg_hover.xml -->
    <item android:drawable="@drawable/item_bg_hover"
        android:state_focused="true" />

    <!-- И в простом состоянии применить item_bg.xml -->
    <item android:drawable="@drawable/item_bg" />
</selector>

После этого можно запускать и смотреть что получилось.

Урок создан: 25 декабря 2013 | Просмотров: 20991 | Автор: Александр Барчук | Правила перепечатки


Добавить комментарий

Добавить комментарий

Ваш e-mail не будет опубликован.

Комментарии:

  • 08 октября 2014 в 01:17

    Dima

    не видно результат выполнения, картинка не грузится

  • 14 января 2015 в 21:36

    Игорь

    Итоговой картинки не видно!!!

  • 31 января 2015 в 13:50

    Серж

    Очень хотелось бы увидеть результат =)

  • 15 апреля 2015 в 15:39

    Владимир

    Здравствуйте. Подскажите как создать элемент ListView чтобы он был ListView (типа список в списке). Или киньте ссылку, плиз )

  • 18 ноября 2015 в 22:18

    Ильшат

    Очень долго ищу материал, как сделать ListView с выпадающим текстом при нажатии на любой из пунктов и при возможности рассказать как привязать к нему searchView и использовать в качестве фильтра слов. Заранее и вообщем Вам спасибо Александр!

    • 04 мая 2016 в 05:45

      Сергей

      ExpandableListView — то, что вам нужно

  • 10 февраля 2016 в 20:43

    Костя

    Спасибо, мне очень помог этот урок.

  • 14 апреля 2016 в 01:44

    Вадим

    Вы вывели ListView, но как в самом приложении реализовать чтоб в приложении «ToDo» по нажатию кнопки «+» добавлялись элементы?