Статьи

Пользовательский интерфейс Android: понимание представлений

Эта статья является частью нашего Академического курса под названием Android UI Design — Основы .

В этом курсе вы познакомитесь с основами дизайна пользовательского интерфейса Android. Вы поймете пользовательский ввод, представления и макеты, а также адаптеры и фрагменты. Проверьте это здесь !

1. Обзор

В нашей предыдущей статье мы представили некоторые основные понятия о шаблонах пользовательского интерфейса Android и увидели, как мы можем создать согласованный пользовательский интерфейс, следуя этим шаблонам. В этой статье мы хотим больше узнать о Views и о том, как мы можем использовать их для создания великолепных пользовательских интерфейсов. Эта статья будет охватывать понятия о Views и Adapters . Разработка пользовательского интерфейса в Android может быть довольно простой, поскольку мы можем описать их с помощью файлов XML, и система выполнит тяжелую работу, преобразовав их в реальные компоненты пользовательского интерфейса, разместив их в соответствии с размером экрана и разрешением.

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

2. Просмотров

Класс View является базовым классом, который расширяют все компоненты. View рисует что-то на экране и отвечает за обработку событий, когда пользователь взаимодействует с ним. Даже общий класс ViewGroup расширяет View . ViewGroup является специальным View которое содержит другие представления и размещает эти представления в соответствии с некоторыми правилами. Мы увидим, что Android предоставляет некоторые специализированные представления, которые помогают нам обрабатывать текст, изображения, пользовательский ввод, кнопки и так далее.

Все эти взгляды имеют несколько общих общих аспектов:

  • Все виды имеют набор свойств: эти свойства влияют на способ отображения. Существует набор свойств, общих для всех представлений, в то время как есть некоторые другие свойства, зависящие от типа представления.
  • Фокус: система управляет фокусом на каждом представлении, и в зависимости от пользовательского ввода мы можем изменить и навести фокус на конкретное представление.
  • Прослушиватели: все представления имеют прослушиватели, которые используются для обработки событий, когда пользователь взаимодействует с представлением. Мы можем зарегистрировать наше приложение для прослушивания определенных событий, которые происходят в представлении.
  • Видимость: мы можем контролировать, является ли представление видимым или нет, и мы можем изменить видимость также во время выполнения.

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

  • число
  • строка
  • ссылка на значение, написанное где-то еще

Первые два варианта тривиальны, третий является наиболее интересным, потому что мы можем создать файл (всегда в XML), который содержит список значений, и мы можем ссылаться на него в нашем значении свойства. Это лучший подход, которым нужно следовать, особенно когда мы используем темы и стили или хотим поддерживать многоязычные приложения.

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

Когда мы хотим установить свойство представления, у нас есть два способа сделать это:

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

Например, предположим, что мы хотим определить TextView (он пишет текст). В XML мы имеем:

1
2
3
4
5
<TextView
    android:id="@+id/textView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world" />

Как вы можете заметить, мы определили идентификатор с именем textView1 , некоторые другие свойства, которые имеют строковые значения, и другое свойство с именем android: текст, который ссылается на значение, записанное в другом месте.

Мы могли бы сделать то же самое, используя только строки кода:

1
2
TextView tv = new TextView(this);
tv.setText("blabla");

Эти два подхода эквивалентны; тем не менее, первый в XML является предпочтительным. Существует соответствие между свойством представления XML и методом Java: обычно для каждого свойства XML есть метод set, который мы можем использовать в нашем коде. Вы можете посмотреть здесь, чтобы получить больше информации об этой корреспонденции. В XML свойство представления называется атрибутом.

Двумя свойствами, которые играют важную роль, являются layout_width и layout_height . Эти два свойства определяют, насколько большим и каким должен быть вид. Мы можем использовать два предопределенных значения:

  • MATCH_PARENT
  • WRAP_CONTENT

Значение MATCH_PARENT означает, что мы хотим, чтобы наше представление было таким же большим, как его родитель, который его содержит, а с помощью WRAP_CONTENT мы указываем, что наше представление должно быть достаточно большим, чтобы содержать его содержимое.

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

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

Android SDK имеет несколько типов слушателей, поэтому могут быть реализованы разные интерфейсы. Два из самых популярных: View.OnClickListener , View.OnTouchListener и так далее.

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

2.1. Компонент TextView

Это один из самых простых компонентов. Мы используем его, когда хотим показать текст на экране. Мы видели это раньше:

1
2
3
4
5
<TextView
    android:id="@+id/textView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world" />

где android:text содержит текст, который мы хотим показать. У него есть некоторые атрибуты, которые мы можем использовать, чтобы указать, как мы хотим показать текст:

  • android:fontFamily — это семейство шрифтов.
  • android:shadowColor — это цвет тени.
  • android:shadowDx — это смещение оси х тени.
  • android:shadowDy — это смещение оси y тени.
  • android:textStyle — это стиль (полужирный, курсив, полужирный).
  • android:textSize — это размер текста.

Ниже приведены некоторые примеры TextView :

фигура 1

фигура 1

и соответствующий XML:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<TextView
    android:id="@+id/textView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Android UI" />
<TextView
    android:id="@+id/textView2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Android UI"
    android:textStyle="italic" />
<TextView
    android:id="@+id/textView2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Android UI"
    android:textColor="#FFFFFF"
    android:textSize="15dp"
    android:textStyle="bold" />

2.2. Компонент ImageView

Этот компонент используется для отображения изображения на экране. Изображение, которое мы хотим показать, может быть помещено в наш apk или мы можем загрузить его удаленно. В первом случае наше изображение должно быть помещено в каталог res/drawable . Основываясь на том, что мы обсуждали в предыдущей статье , мы уже знаем, что должны помнить, что наше приложение может работать на нескольких устройствах с разной плотностью экрана.

Чтобы лучше поддерживать разную плотность экрана, мы можем создавать разные изображения с разным разрешением. Каталог res/drawable является каталогом по умолчанию, куда система смотрит, если не может найти изображение с правильной плотностью для экрана. Вообще говоря, нам нужно создать как минимум четыре разных изображения с разным разрешением, на самом деле мы можем заметить в нашей IDE, что есть как минимум пять каталогов:

  • drawable-ldpi (больше не поддерживается)
  • drawable-mdpi (средний точек на дюйм)
  • drawable-hdpi (высокое разрешение)
  • drawable-xhdpi (сверхвысокий dpi)
  • drawable-xxhdpi (x-extra-high dpi)
  • drawable

Важно помнить следующее: изображения должны иметь одинаковые имена. Получив его, мы можем использовать ImageView следующим образом:

1
2
3
4
5
<ImageView
    android:id="@+id/img"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/robot" />

где мы ссылались на изображение, называемое robot.png , используя @drawable/robot (без расширения). Результат показан ниже:

фигура 2

фигура 2

Если изображение должно быть загружено удаленно, при этом необходимо помнить о некоторых соображениях. Во-первых, вы должны учитывать, что загрузка изображения с удаленного сервера является трудоемкой операцией, и вы должны делать это в отдельном потоке, чтобы избежать проблемы ANR (приложение не отвечает). Если вы хотите, чтобы все было просто, вы можете использовать AsyncTask или Volley lib. Второе, что нужно помнить, это то, что вы не можете установить изображение в XML-файле, но вы можете определить только ImageView без ссылки на изображение. В этом случае вы можете установить изображение, используя:

1
img.setImageBitmap(your_bitmap)

где your_bitmap — это изображение, которое вы загрузили удаленно.

2,3. Элементы управления вводом

Input controls — это компоненты, с которыми пользователь может взаимодействовать. Используя такой компонент, вы можете, например, дать пользователю возможность вставить некоторые значения. Инфраструктура пользовательского интерфейса Android предоставляет широкий спектр элементов управления вводом: text field , text field input field , toggle buttons , radio buttons , buttons , checkbox , средства buttons и так далее. Каждый из них имеет специализированный класс, и мы можем создавать сложные интерфейсы, используя эти компоненты.
Ниже приведен список общих компонентов с классом, который его обрабатывает:

Составная часть Описание Класс
Button Это один из самых используемых компонентов. Он может быть нажат пользователем, а при нажатии мы можем запустить действие. кнопка
TextFields Это редактируемый текст, и мы даем пользователю возможность вставить некоторые данные. EditText — это классическое поле ввода, а AutoCompleteTextView — это компонент, который мы можем использовать, когда у нас есть заранее определенный список результатов, и мы хотим, чтобы система завершила некоторые слова, вставленные пользователем. Редактировать текст
AutoCompleteTextView
CheckBox Это компонент вкл / выкл. Мы можем использовать его, когда хотим, чтобы пользователь выбрал один или несколько вариантов. CheckBox
Radio buttons Очень похоже на флажок за исключением того, что пользователь может выбрать только один элемент. RadioGroup
Переключатель
Toggle button Компонент включения / выключения с индикатором состояния. Кнопка-переключатель
Pickers Компонент, который помогает пользователю выбрать одно значение с помощью кнопок вверх / вниз или жеста. Обычно мы используем DatePicker и TimePicker DatePicker
TimePicker

Таблица 1

Мы хотим сфокусировать наше внимание на компоненте Button который широко используется в пользовательском интерфейсе Android. Сначала мы должны определить наш пользовательский интерфейс, используя файл XML:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
 
    <Button
        android:id="@+id/Btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Click here!" />
 
</RelativeLayout>

Обратите внимание, что в идентификаторе кнопки мы использовали @+id означает, что мы добавляем новый идентификатор, а атрибут android:text — это строка, которую мы хотим показать на кнопке. Вы можете использовать ссылку вместо того, чтобы писать текст напрямую. Если мы создадим простое приложение с использованием этого макета и запустим его, у нас будет:

Рисунок 3

Рисунок 3

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
 
    <Button
        android:id="@+id/Btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Click here!" />
 
    <CheckBox
        android:id="@+id/checkBox1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/Btn"
        android:layout_below="@+id/Btn"
        android:layout_marginTop="29dp"
        android:text="CheckBox" />
 
    <ToggleButton
        android:id="@+id/toggleButton1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/checkBox1"
        android:layout_below="@+id/checkBox1"
        android:layout_marginTop="20dp"
        android:text="ToggleButton" />
 
    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/toggleButton1"
        android:layout_centerVertical="true"
        android:ems="10"
        android:hint="Your name here" >
 
        <requestFocus />
    </EditText>
 
    <Switch
        android:id="@+id/switch1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/toggleButton1"
        android:layout_alignBottom="@+id/toggleButton1"
        android:layout_alignParentRight="true"
        android:text="Switch" />
 
    <RadioGroup
        android:id="@+id/radioGroup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/editText1"
        android:layout_below="@+id/editText1"
        android:layout_marginTop="24dp" >
 
        <RadioButton
            android:id="@+id/radioButton1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/editText1"
            android:layout_below="@+id/editText1"
            android:layout_marginTop="40dp"
            android:text="Option 1" />
 
        <RadioButton
            android:id="@+id/radioButton2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/radioButton1"
            android:layout_below="@+id/radioButton1"
            android:text="Option 2" />
    </RadioGroup>
 
</RelativeLayout>

В этом интерфейсе мы использовали почти все компоненты пользовательского интерфейса, и если мы запустим приложение, у нас будет:

Рисунок 4

Рисунок 4

3. События пользовательского интерфейса и слушатели

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

Listeners являются важным аспектом при разработке пользовательского интерфейса в Android. Вообще говоря, когда пользователь взаимодействует с интерфейсом нашего приложения, система «создает» некоторые события. Каждое представление, которое создает наш интерфейс, способно генерировать события и предоставлять средства для их обработки.

С точки зрения API, если мы посмотрим на класс View , мы можем заметить, что есть несколько открытых методов; эти методы вызываются системой при возникновении некоторых событий. Они называются методами callback , и они являются способом, которым мы можем фиксировать события, которые происходят, когда пользователь взаимодействует с нашим приложением. Обычно эти методы обратного вызова начинаются с on + event_name .

Каждое View предоставляет набор методов обратного вызова. Некоторые являются общими для нескольких представлений, в то время как другие являются специфичными для представлений. В любом случае, чтобы использовать этот подход, если мы хотим поймать событие, мы должны расширить View и это явно не практично. По этой причине каждое View имеет набор интерфейсов, которые мы должны реализовать, чтобы получать уведомления о происходящих событиях. Эти интерфейсы предоставляют набор методов обратного вызова. Мы называем эти интерфейсы слушателями событий. Поэтому, например, если мы хотим получать уведомления, когда пользователь нажимает кнопку, у нас есть интерфейс для реализации, который называется View.onClickListener .

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

1
Button b = (Button) findViewById(R.id.btn);

В нашей Activity есть метод findViewById который можно использовать для получения ссылки на представление, определенное в нашем пользовательском интерфейсе. Получив ссылку, мы можем обработать щелчок пользователя:

1
2
3
4
5
6
b.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // here we handle the event            
    }
});

Как вы можете видеть, мы использовали компактную форму, и вы можете заметить метод обратного вызова onClick . Этот метод вызывается системой, когда пользователь нажимает на нашу кнопку, поэтому здесь мы должны обработать событие. Мы могли бы получить тот же результат другим способом: мы можем заставить нашу Activity реализовать интерфейс View.OnClickListener и снова реализовать View.OnClickListener . Запустив приложение мы имеем в итоге:

Рисунок 5

Рисунок 5

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

4. Разработка пользовательского интерфейса

Ранее мы коротко представили концепцию Activity . Настало время лучше объяснить его функцию. Пользовательский интерфейс Android не может существовать, если нет контейнера, в котором он содержится. Этот контейнер называется Activity и это одна из самых важных концепций в Android. Каждое приложение, которое мы создадим, имеет по крайней мере одно действие, и обычно более сложные приложения имеют несколько действий, которые взаимодействуют друг с другом, обмениваясь данными и информацией с помощью Intents . Activity заботится о создании окна, где мы размещаем наш пользовательский интерфейс.

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

Первое, что нужно создать макет. Мы предполагаем, что вы уже знаете, как создать проект Android. Если нет, пожалуйста, обратитесь к нашей статье «Android Hello World» . В Android (то есть с использованием Eclipse в качестве IDE) мы должны поместить файлы XML в res/layout . Если вы посмотрите внимательно, вы можете заметить, что есть разные подкаталоги с разными именами и расширениями. К настоящему времени достаточно, чтобы мы добавили наши файлы в res/layout (каталог по умолчанию). Другие директории будут использоваться в зависимости от типа устройства, так что у нас есть возможность оптимизировать наш интерфейс в соответствии с размером экрана и ориентацией.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/edt"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
 
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Please insert your username" />
 
    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_below="@+id/textView1"
        android:ems="10" />
 
    <Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="16dp"
        android:text="Confirm" />
 
</RelativeLayout>
1
activity_main.xml

Далее мы должны создать деятельность. Если мы используем IDE, мы заметим, что она уже создала класс по умолчанию (в Eclipse она называется MainActivity.java ). Смотря его исходный код, есть метод onCreate . Здесь первое, что мы делаем, это «привязываем» наш пользовательский интерфейс к активности:

1
setContentView(R.layout.activity_main);

Далее мы можем обработать событие click: когда пользователь щелкает, мы можем получить значение EditText и показать сообщение:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
// Lookup Button reference
Button b = (Button) findViewById(R.id.btn1);
         
// Lookup EditText reference
final EditText edt = (EditText) findViewById(R.id.editText1);
     
b.setOnClickListener(new View.OnClickListener() {
             
    @Override
    public void onClick(View v) {              
        String txt = edt.getText().toString();                 
        Toast.makeText(MainActivity.this, "Hello " + txt, Toast.LENGTH_LONG).show();
    }
});

Запустив приложение мы получим:

Рисунок 6

Рисунок 6

Это очень простой пример, но полезно иметь представление о том, как мы можем создать пользовательский интерфейс.

5. Вид и адаптеры

К настоящему времени мы говорили о компонентах пользовательского интерфейса, которые показывают только одно значение. Некоторые компоненты могут отображать несколько значений или набор данных. Этот тип управления называется List управления. Если мы хотим использовать эти элементы управления в Android, мы не можем использовать один компонент, как мы делали раньше, но мы должны использовать два компонента: один, который управляет отображением данных, и другой, который управляет основными данными. Последний называется Adapter . AdapterView управления списком расширяет AdapterView а самые «известные» элементы управления списком:

  • Посмотреть список
  • волчок
  • Вид сетки
  • Галерея

Компонент ListView отображает свой элемент в списке. Это очень часто используется в пользовательском интерфейсе Android.

Spinner — это еще один компонент, который отображает только один элемент, но позволяет пользователю выбирать из нескольких элементов.

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

Gallery показывает элемент в горизонтальной прокрутке. Этот компонент устарел начиная с уровня API 16. Мы сосредоточим наше внимание на первых двух компонентах, поскольку они очень полезны при создании пользовательских интерфейсов.

Глядя на API, AdapterView расширяет ViewGroup так что они оба косвенно расширяют класс View . Расширение ViewGroup означает, что элемент управления списком содержит список представлений как дочерние, и в то же время это тоже представление. Роль адаптера заключается в управлении данными и предоставлении соответствующего дочернего представления в соответствии с данными, которые должны отображаться. Android предоставляет несколько универсальных адаптеров. Данные могут быть загружены из базы данных, из ресурсов или мы можем создавать их динамически.

Предположим, мы хотим создать пользовательский интерфейс, в котором мы отображаем список элементов. Давайте предположим, что элементы просто строки. В этом случае первое, что мы должны сделать, это создать ArrayAdapter . Это один из наиболее часто используемых адаптеров, поскольку он очень прост в использовании. Мы можем использовать готовую компоновку для наших детей, чтобы нам не пришлось слишком много работать. Android предоставляет несколько готовых макетов, готовых к использованию.

Интерфейс нашего приложения в XML выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
 
    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
 
</RelativeLayout>

Теперь нам нужно создать адаптер, чтобы мы могли отображать элементы:

1
2
ArrayAdapter<String> aAdpt = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
 new String[]{"Froyo", "Gingerbread", "Honeycomb""Ice cream Sandwich", "Jelly  Bean", "KitKat"});

Таким образом, мы создали ArrayAdapter используя в качестве дочернего макета android.R.layout.simple_list_item_1 , макет, предоставляемый Android, и мы создали список строк в качестве данных. Теперь нам нужно добавить адаптер в просмотр списка:

1
2
ListView lv = (ListView) findViewById(R.id.list);
lv.setAdapter(aAdpt);

Запустив приложение, мы имеем:

Рисунок 7

Рисунок 7

В приведенном выше примере мы предоставили элементы в виде списка строк, написанных непосредственно в исходном коде. Мы можем предоставить такой список в файле ресурсов. Мы можем предположить, что у нас есть компонент Spinner его дочерний элемент извлекается из файла ресурсов. Сначала мы создаем наш ArrayAdapter :

1
ArrayAdapter<CharSequence> aAdpt = ArrayAdapter.createFromResource(this, R.array.days,  android.R.layout.simple_spinner_item);

где R.array.days — наш файл ресурсов массива (содержащий названия дней), а android.R.layout.simple_spinner_itemR.array.days макет. Затем мы прикрепляем адаптер к блесне:

1
2
aAdpt.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);      
spinner.setAdapter(aAdpt);

Запустив приложение, мы имеем:

Рисунок 8

Рисунок 8

В некоторых случаях информация для отображения хранится в базе данных. В этом случае Android предоставляет некоторый специализированный адаптер, который помогает нам читать данные из БД и заполнять соответствующий элемент управления списком. Этот адаптер называется CursorAdapter или, если мы предпочитаем, мы можем использовать SimpleCursorAdapter . Например, мы можем использовать этот тип адаптера, когда мы хотим заполнить ListView контактами, хранящимися в нашем смартфоне. В этом случае мы можем использовать CursorAdapter .

5.1. Обработка событий ListView

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

01
02
03
04
05
06
07
08
09
10
11
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView<?> aView, View view, int position,long id) {             
    // We handle item click event
    TextView tv = (TextView) view;
    String text = (String) tv.getText();
                 
    Toast.makeText(MainActivity.this, "You clicked on " + text, Toast.LENGTH_LONG).show();
                 
    }
 });

Анализируя приведенный выше код, мы просто устанавливаем прослушиватель onItemCick элементов, и в этом случае мы должны переопределить метод с именем onItemCick . В этом методе мы получаем, в качестве параметра, AdapterView , представление, по AdapterView , положение внутри ListView и его идентификатор. Мы уже знаем, что View является TextView потому что мы использовали ArrayAdapter строк, чтобы мы могли безопасно преобразовать представление в текстовое представление. Как только мы узнаем, что TextView нажал, мы можем получить текст.

Рисунок 9

Рисунок 9

5.2. Пользовательский шаблон адаптера и держателя просмотра

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

Во всех этих случаях мы можем реализовать пользовательский адаптер: вы можете решить расширить абстрактный класс BaseAdapter или вы можете расширить какой-нибудь специализированный класс, например, ArrayAdapter . Выбор зависит от типа информации, которую вы должны обработать. Чтобы упростить задачу и сосредоточить наше внимание на пользовательском адаптере, мы предположим, что мы хотим создать ListView, в котором дочерние представления имеют две текстовые строки.

Сначала мы создаем наш адаптер и начинаем реализовывать некоторые методы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class CustomAdapter extends BaseAdapter {
 
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return 0;
    }
 
    @Override
    public Object getItem(int arg0) {
        // TODO Auto-generated method stub
        return null;
    }
 
    @Override
    public long getItemId(int arg0) {
        // TODO Auto-generated method stub
        return 0;
    }
 
    @Override
    public View getView(int arg0, View arg1, ViewGroup arg2) {
        // TODO Auto-generated method stub
        return null;
    }
}

Мы должны реализовать четыре метода. Прежде чем сделать это, мы определяем класс, который является нашей моделью данных. Для простоты мы можем предположить, что у нас есть класс Item с двумя открытыми атрибутами: name и descr . Теперь нам нужен дочерний макет, чтобы мы могли отображать данные:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
 
    <TextView
        android:id="@+id/descr"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
 
</LinearLayout>
1
item_layout.xml

В нашем адаптере нам нужен конструктор, чтобы мы могли передавать данные, которые мы хотим отобразить:

1
2
3
4
public CustomAdapter(Context ctx, List<Item> itemList) {
    this.ctx = ctx;
    this.itemList = itemList;
}

Теперь переопределить метод довольно просто, кроме getView :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Override
public int getCount() {
    return itemList == null ? 0 : itemList.size();
}
 
@Override
public Object getItem(int pos) {
    return itemList == null ? null : itemList.get(pos);
}
 
@Override
public long getItemId(int pos) {
    return itemList == null ? 0 : itemList.get(pos).hashCode();
}

Обратите внимание, что в getItemId мы возвращаем уникальное значение, которое представляет элемент. Немного getView метод getView . Это сердце нестандартного адаптера, и мы должны тщательно его реализовать. Этот метод вызывается каждый раз, когда ListView должен отображать дочерние данные. В этом методе мы можем создать представление вручную или накачать файл XML:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
 
     if (v == null) {
    LayoutInflater lInf = (LayoutInflater)
        ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    v = lInf.inflate(R.layout.item_layout, null);          
    }
         
  TextView nameView = (TextView) v.findViewById(R.id.name);
  TextView descrView = (TextView) v.findViewById(R.id.descr);
         
  nameView.setText(itemList.get(position).name);
  descrView.setText(itemList.get(position).descr);
  return v;
}

Анализируя код, мы можем заметить, что сначала мы проверяем, что представление, переданное в качестве параметра, не является нулевым. Это необходимо, потому что система Android может повторно использовать представление, чтобы не тратить ресурсы. Поэтому, если представление не является нулевым, мы можем избежать раздувания файла макета XML. Если это ноль, тогда мы должны надуть файл макета. Следующим шагом является поиск TextView внутри нашего макета, чтобы мы могли установить их значения. Запустив приложение, мы получим:

Рисунок 10

Рисунок 10

Глядя на метод getView , мы можем заметить, что мы вызываем findViewById несколько раз. Это дорогостоящая операция, и мы должны избегать ее вызова, когда в ней нет необходимости, потому что она может замедлить общую производительность Listview а когда пользователь прокручивает ListView это может произойти не гладко.

Кроме того, мы используем findViewById даже если представление перерабатывается. В этой ситуации мы можем применить шаблон, который уменьшает использование этого метода. Этот шаблон называется View Holder , он требует, чтобы мы создали объект для хранения ссылок на представления внутри нашего дочернего макета. Применяя этот шаблон, метод getView становится:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    TextHolder th = null;
         
    if (v == null) {
        LayoutInflater lInf = (LayoutInflater)
                ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = lInf.inflate(R.layout.item_layout, null);  
             
        TextView nameView = (TextView) v.findViewById(R.id.name);
        TextView descrView = (TextView) v.findViewById(R.id.descr);
             
        th = new TextHolder();
        th.nameView = nameView;
        th.descrView = descrView;
        v.setTag(th);
    }
    else
        th = (TextHolder) v.getTag();
         
    th.nameView.setText(itemList.get(position).name);
    th.descrView.setText(itemList.get(position).descr);
         
    return v;
}
 
     
static class TextHolder {
    TextView nameView;
    TextView descrView;
}

6. Загрузите исходный код

Это был урок использования Android Views. Вы можете скачать исходный код здесь: AndroidView.zip