Evgenii Legotckoi
Evgenii LegotckoiШілде 21, 2015, 9:55 Т.Ж.

Android. Теңшеу тізім көрінісі

Кастомизация ListView является обычным явлением при разработке Приложения под ОС Android. ListView (Список) позволяет отображать однотипную информацию в Вашем Приложении, например показывать товары с ценами и характеристиками, если вы разрабатываете Приложение для онлайн продаж. При этом являясь по сути рутинной операцией, тем не менее, некоторые вопросы доставляют начинающим разработчикам массу неудобств. Некоторые из этих неудобств и вопросов будут разобраны в данной статье.

Структура проекта

В данном уроке предлагается создать небольшой список с флагами стран, названием валют этих стран и аббревиатурой этих стран.

Всего для создания списка используется четыре страны: Россия, Южная Африка, Сингапур, Турция. Флаги этих стран находятся в папке drawable. Также присутствует xml файл для создания бэкграунда ListViewи флагов. Для хранения названий и аббревиатур используется файл strings.xml. Также используется отдельный xml файл для создания разметки элемента ListView.

В Проекте присутствует три класса:

  1. MainActivity
  2. MyListAdapter - который отвечает за передачу данных в форму элемента списка
  3. DataFlags - класс, который призван облегчить передачу данных в Адаптер ListView

Формирование разметки элемента ListView

Для создания приятного глазу списка в данном уроке использовались следующие файлы:

  1. list_item.xml
  2. flag.xml
  3. listview_selector.xml
  4. styles.xml

listview.xml

В данном файле приводится разметка непосредственно элемента listview.

<?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="48dp"
    android:background="@drawable/listview_selector"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/flagImage"
        android:src="@drawable/flag_russian"
        android:layout_width="48dp"
        android:layout_height="40dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="8dp"
        android:layout_marginEnd="8dp"
        android:contentDescription="@string/flag"
        android:background="@drawable/flag"
        android:layout_gravity="center_vertical"/>

    <TextView
        android:id="@+id/abbreviation"
        android:layout_width="52dp"
        android:layout_height="30dp"
        android:textColor="@color/black"
        android:textSize="24sp"
        android:text="@string/russianRUB"
        android:layout_marginRight="6dp"
        android:layout_marginEnd="6dp"
        android:layout_gravity="center_vertical"/>

    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/black"
        android:textSize="14sp"
        android:text="@string/russian"
        android:gravity="center"
        android:layout_gravity="center"
        android:singleLine="false"/>

</LinearLayout>

flag.xml

В данном файле создан backgroud для ImageView, в котором будет отображаться флаг. Края подложки для флага скруглены. А также настроен цвет подложки.

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

    <solid android:color="@color/blue_grey_100" />

    <padding
        android:bottom="2dp"
        android:top="2dp"
        android:right="2dp"
        android:left="2dp"/>

    <corners android:radius="4dp"/>

</shape>

listview_selector.xml

Данная разметка используется как background для элемента ListView. В данном файле настраивается цвет фона элемента в нормальном режиме и при нажатии на элемент списка.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_selected="false"
        android:state_pressed="false"
        android:drawable="@color/white" />

    <item
        android:state_pressed="true"
        android:drawable="@color/blue_grey_100" />

    <item
        android:state_selected="true"
        android:state_pressed="false"
        android:drawable="@color/blue_grey_100" />

</selector>

styles.xml

Файл стилей знаком всем, кто занимается разработкой под Android. В данном случае он используется только лишь для хранения кодов цветов, используемых в данном примере.

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    </style>

    <color name="white">#ffffff</color>
    <color name="black">#000000</color>
    <color name="blue_grey_100">#CFD8DC</color>

</resources>

activity_main.xml

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listViews" />

</RelativeLayout>

Основной класс проекта - MainActivity.java

В Данном классе производится запуск основной активити проекта, в которой будет отображаться кастомный ListView. Данные в ListView передаются с помощью списка ArrayList, в который помечаются объекты данных для каждого элемента. Группировка данных для элемента ListView производится с помощью отдельного класса, формирующего структуру данных, в которой будут храниться ID изображения флага, ID строки названия валюты страны, а также ID строки Аббревиатур валют стран.

По сути в этом классе производится стандартная настройка ListView с тем отличием, что используется не обычный List, а ArrayList, в который передается объект, а не единичная строка.

package ru.evileg.listview;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;

import java.util.ArrayList;


public class MainActivity extends ActionBarActivity {

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

        // Находим ListView в разметке активити
        ListView listView = (ListView) this.findViewById(R.id.listViews);

        // Создаём ListView, в котором будут храниться данные для списка
        ArrayList<DataFlags> list = new ArrayList<>();
        list.clear();

        /* Наполняем List данным. В данном случае данные передаются Объектом,
         * в котором хранятся id, указывающие на флаги в ресурсах, а также
         * на Строки, в которых прописаны названия валют стран и аббревиатуры этих валют */
        list.add(new DataFlags(R.drawable.flag_russian, R.string.russian, R.string.russianRUB));
        list.add(new DataFlags(R.drawable.flag_africa, R.string.africa, R.string.africaZAR));
        list.add(new DataFlags(R.drawable.flag_singapore, R.string.singapore, R.string.singaporeSGD));
        list.add(new DataFlags(R.drawable.flag_turkish, R.string.turkish, R.string.turkishTRY));

        /* Создаём адаптер, который будет передавать данные из List`а в
         * элемент ListView и подключаем его непосредственно к ListView`у
         */
        MyListAdapter myListAdaptery = new MyListAdapter(this, list);
        listView.setAdapter(myListAdaptery);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Все строки, которые применяются для ListView хранятся непосредственно в файле ресурсов strings.xml

<resources>
    <string name="app_name">ListView</string>
    <string name="action_settings">Settings</string>

    <string name="africaZAR">ZAR</string>
    <string name="russianRUB">RUB</string>
    <string name="singaporeSGD">SGD</string>
    <string name="turkishTRY">TRY</string>

    <string name="africa">South African\nrand</string>
    <string name="russian">Russian\nrouble</string>
    <string name="singapore">Singapore\ndollar</string>
    <string name="turkish">Turkish\nlira</string>

    <string name="flag">flag</string>
</resources>

Класс DataFlags.java

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

package ru.evileg.listview;

/**
 * Application ListView
 * Created by EvILeg on 21.07.2015.
 */
public class DataFlags {

    private int flagID;
    private int nameID;
    private int abbreviationID;

    public DataFlags (int _flagID, int _nameID, int _abbreviationID){
        flagID = _flagID;
        nameID = _nameID;
        abbreviationID = _abbreviationID;
    }

    public int getFlagID(){
        return flagID;
    }

    public int getNameID(){
        return nameID;
    }

    public int getAbbreviationID() {
        return abbreviationID;
    }
}

Класс MyListAdapter

Самый важный класс в данном уроке. Именно этот класс служит для передачи данных из ArrayList в ListView. Данный класс расширяет возможности родительского класса BaseAdapter.

package ru.evileg.listview;

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

import java.util.ArrayList;

/**
 * Application ListView
 * Created by EvILeg on 21.07.2015.
 */
public class MyListAdapter extends BaseAdapter {

    /*
     * Создаем объекты для отображения внешнего вида элемента
     * и объекта списка, с которым будет производиться работа
     */
    private LayoutInflater LInflater;
    private ArrayList<DataFlags> list;

    /*
     * Конструктор класса. В данном случае лишь транслируется лист с данными
     * в лист адаптера, с которым будет производиться непосредственная работа
     */
    public MyListAdapter(Context context, ArrayList<DataFlags> data){

        list = data;
        LInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    /*
     * Далее идут стандартные методы родительского класса BaseAdapter.
     * Внимательно ознакомьтесь с отличиями методов в уроке и методов,
     * которые автоматически создает Android Studio.
     * Данные методы должны работать непосредственно с используемым нами ArrayList
     * и структурой данных, формируемой классом DataFlags
     */
    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public DataFlags getItem(int position) {
        return list.get(position);
    }

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

    /*
     * Метод, в котором формируется внешний вид элементов с его наполнением
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder holder;
        View v = convertView;

        /*
         * В том случае, если вид элемента не создан, производится его создание
         * с помощью ViewHolder и тегирование данного элемента конкретным holder объектом
         */
        if ( v == null){
            holder = new ViewHolder();
            v = LInflater.inflate(R.layout.list_item, parent, false);
            holder.flag = (ImageView) v.findViewById(R.id.flagImage);
            holder.name = (TextView) v.findViewById(R.id.name);
            holder.abbreviation = ((TextView) v.findViewById(R.id.abbreviation));
            v.setTag(holder);
        }

        /*
         * После того, как все элементы определены, производится соотнесение
         * внешнего вида, данных и конкретной позиции в ListView.
         * После чего из ArrayList забираются данные для элемента ListView и
         * передаются во внешний вид элемента
         */
        holder = (ViewHolder) v.getTag();
        DataFlags dataFlags = getData(position);

        holder.flag.setImageResource(dataFlags.getFlagID());
        holder.name.setText(v.getResources().getString(dataFlags.getNameID()));
        holder.abbreviation.setText(v.getResources().getString(dataFlags.getAbbreviationID()));

        return v;
    }

    /*
     * Метод, который забирает объект из ArrayList для дальнейшей работы с ним
     * и передачи его данных в элемент ListView
     */
    DataFlags getData(int position){
        return (getItem(position));
    }

    /*
     * Данная структура данных необходима для того, чтобы при пролистывании
     * большого списка не возникало артефактов и перескакивания данных с одной позиции ListView
     * на другую, что достигается тегированием каждого элемента ListView
     */
    private static class ViewHolder {
        private ImageView flag;
        private TextView name;
        private TextView abbreviation;
    }
}

Использование ViewHolder принципиально важно, поскольку, если не применять данный прием, можно получить неадекватной поведение ListView, при котором будет происходит неконтролируемое перескакивание данных с одной позиции ListView на другую. Причем это будет происходит только внешне, а на самом деле данные будут оставаться на своих местах. Будет возникать несоответствие внешнего вида ListView с его содержанием.

Итог

Если в процессе изучения урока не возникло никаких проблем и ошибок, то внешний вид полученного ListView должен быть подобен следующему:

Кастомный ListView

Кастомный ListView с нажатым элементом

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

ДВ
  • Наурыз 9, 2019, 6:53 Т.Ж.
  • (өңделген)

Спасибо за пример.
У меня возникает ошибка.

AndroidRuntime: FATAL EXCEPTION: main
Подскажите, пожалуйста, с чем это может быть связано?

Evgenii Legotckoi
  • Наурыз 11, 2019, 4:32 Т.Ж.

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

AndroidRuntime: FATAL EXCEPTION: main

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

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
Г

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:66ұпай,
  • Бағалау ұпайлары-1
t

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:33ұпай,
  • Бағалау ұпайлары-10
t

Qt - Тест 001. Сигналы и слоты

  • Нәтиже:52ұпай,
  • Бағалау ұпайлары-4
Соңғы пікірлер
G
GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssrАқп. 8, 2024, 6:43 Т.Қ.
Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
FynjyШілде 22, 2024, 4:15 Т.Ж.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTМаусым 25, 2024, 1 Т.Ж.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCTМамыр 5, 2024, 5:46 Т.Ж.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Бізді әлеуметтік желілерде бақылаңыз