DRAFT [2019-2020][ua] at 2023-01-05 17:17:43 +0200
Logo-do [errata] Profile

Програмування для мобільних пристроїв

Адаптери та списки

Конспект лекції


Адаптери та списки

Android представляє широку палітру елементів, які представляють списки. Всі вони є спадкоємцями класу android.widget.AdapterView. Це такі віджети

як ListView, GridView, Spinner. Вони можуть виступати контейнерами для інших елементів управління.

 

Адаптери в Android

При роботі зі списками ми маємо справу з трьома компонентами. По-перше, це самі елементи списків (ListView, GridView), які відображають дані. По-

друге, це джерело даних - масив, об'єкт ArrayList, база даних і т.д., в якому знаходяться самі дані. І по-третє, це адаптери - спеціальні компоненти, які

пов'язують джерело даних з елементом списку.

Розглянемо зв'язок елемента ListView з джерелом даних за допомогою одного з таких адаптерів - класу ArrayAdapter.

 

ArrayAdapter

Клас ArrayAdapter є простим адаптером, який пов'язує масив даних з набором елементів TextView, з яких, наприклад, може складатися ListView. Тобто в

даному випадку джерелом даних виступає масив об'єктів. ArrayAdapter викликає у кожного об'єкта метод toString () для приведення до строкового виду і

отриманий рядок встановлює в елемент TextView.

Подивимося на прикладі. Отже, розмітка програми може виглядати так:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">

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

</ListView>

</RelativeLayout>

Тут також визначено елемент ListView, який буде виводити список об'єктів. Тепер перейдемо до коду activity і зв'яжемо ListView через ArrayAdapter з

деякими даними:

package com.example.listapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {

  // набір даних, які зв'яжемо зі списком
  String[] countries = {
    "Бразилія",
    "Аргентина",
    "Колумбія",
    "Чилі",
    "Уругвай"
  };
  @Override

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

    // Отримуємо елемент ListView
    ListView countriesList = (ListView) findViewById(R.id.countriesList);

    // створюємо адаптер
    ArrayAdapter < String > adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, countries);

    // встановлюємо для переліку адаптер 
    countriesList.setAdapter(adapter);

  }

}

Тут спочатку отримуємо по id елемент ListView і потім створюємо для нього адаптер.

Для створення адаптера використовувався наступний конструктор ArrayAdapter <String> (this, android.R.layout.simple_list_item_1, countries), де

В кінці неоходімо встановити для ListView адаптер за допомогою методу setAdapter (). У підсумку ми отримаємо наступне відображення:

 

Ресурс string-array і ListView

У попередньому прикладі було розглянуто, як виводити масив рядків за допомогою ArrayAdapter в ListView. При цьому масив рядків визначався

програмно в коді java. Однак подібний масив рядків набагато зручніше було б зберігати в файлі xml у вигляді ресурсу.

Ресурси масивів рядків представляють елемент типу string-array. Вони можуть знаходиться в каталозі res / values в xml-файлі з довільним ім'ям.

Визначення масивів рядків мають наступний синтаксис:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="ім'я_масиву_рядків">
<item>елемент</item>
</string-array>
</resources>

Масив рядків задається за допомогою елемента <string-array>, атрибут name може мати довільне значення, за яким потім будуть посилатися на цей

масив.

Всі елементи масиву представляють набір значень <item>.

Наприклад, додамо в папку res / values новий файл. Для цього натиснемо правою кнопкою миші на даний каталог і меню виберемо пункт New -> Value

resource file:

У вікні назвемо файл як countries:

 

После добавления файла в папку res/values изменим его содержимое следующим образом:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="countries">
<item>Бразилія</item>
<item>Аргентина</item>
<item>Колумбія</item>
<item>Чилі</item>
<item>Уругвай</item>
</string-array>
</resources>

У файлі розмітки activity_main.xml залишається визначення елемента ListView:

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">

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

</RelativeLayout>

Тепер зв'яжемо ресурс і ListView в коді MainActivity:

package com.example.listapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {
  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Отримуємо елемент ListView
    ListView countriesList = (ListView) findViewById(R.id.countriesList);

    // Отримуємо ресурс
    String[] countries = getResources().getStringArray(R.array.countries);

    // створюємо адаптер
    ArrayAdapter < String > adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, countries);

    // встановлюємо для переліку адаптер 
    countriesList.setAdapter(adapter);

  }

}

Для отримання ресурсу в коді java застосовується вираз R.array.назва_ресурса.

Але нам необязателно додавати список рядків в ListView програмно. У цього елемента є атрибут entries, який в якості значення може приймати ресурс

string-array:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ListView
android:entries="@array/countries"
android:id="@+id/countriesList"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>

</RelativeLayout>

В цьому випадку код MainActivity ми можемо скоротити до стандартного:

package com.example.listapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

  @Override

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

  }

}

А результат буде той же самий:

 

 

Вибір елемента в ListView

У минулих темах було розглянуто, як можна завантажувати дані в ListView, пов'язувати його з джерелом даних. Але крім простого виведення списку

елементів ListView дозволяє вибирати елемент і обробляти його вибір. Розглянемо, як це зробити. Визначимо наступну розмітку в файлі

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:id="@+id/selection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="22sp" />

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

</LinearLayout>

Тепер зв'яжемо список ListView з джерелом даних і закріпимо за ним слухач натискання на елемент списку:

package com.example.listapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;

import android.widget.AdapterView;

import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

import android.widget.AdapterView.OnItemClickListener;
public class MainActivity extends AppCompatActivity {

  String[] countries = {
    "Бразилія",
    "Аргентина",
    "Колумбія",
    "Чилі",
    "Уругвай"
  };
  private TextView selection;

  @Override

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

    // Отримуємо елемент TextView
    selection = (TextView) findViewById(R.id.selection);

    // Отримуємо елемент ListView
    ListView countriesList = (ListView) findViewById(R.id.countriesList);

    // створюємо адаптер

    ArrayAdapter < String > adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, countries);

    // встановлюємо для переліку адаптер 
    countriesList.setAdapter(adapter);

    // Додаємо для списку слухач 
    countriesList.setOnItemClickListener(new OnItemClickListener() {

      @Override

      public void onItemClick(AdapterView < ? > parent, View v, int position, long id)

      {

        // за позицією отримуємо обраний елемент 
        String selectedItem = countries[position];

        // встановлення тексту елемента 
        TextView selection.setText(selectedItem);

      }

    });

  }

}

Отже, метод setAdapter пов'язує елемент ListView з певним адаптером. Далі для обробки вибору елемента списку встановлюється слухач

OnItemClickListener. Цей слухач має один метод onItemClick, через параметри якого ми можемо отримати виділений елемент і супутні дані. Так, він

приймає такі параметри:

У підсумку, отримуючи індекс натиснутого віджета, який відповідає індексу елемента в масиві рядків, ми встановлюємо його текст як текст елемента

TextView (selection.setText (countries [position])).

Множинний вибір в списку

Іноді потрібно вибрати не один елемент, як за замовчуванням, а кілька. Для цього, по-перше, в розмітці списку треба встановити атрибут android:

choiceMode = "multipleChoice":

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:id="@+id/selection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="22sp" />

<ListView
android:choiceMode="multipleChoice"
android:id="@+id/countriesList"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</LinearLayout>

Тепер визначимо в коді MainActivity обробку вибору елементів списку:

package com.example.listapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.util.SparseBooleanArray;
import android.view.View;

import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

import android.widget.AdapterView.OnItemClickListener;
public class MainActivity extends AppCompatActivity {

  String[] countries = {
    "Бразилія",
    "Аргентина",
    "Колумбія",
    "Чилі",
    "Уругвай"
  };
  TextView selection;

  ListView countriesList;
  @Override

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

    // Отримуємо елемент TextView
    selection = (TextView) findViewById(R.id.selection);

    // Отримуємо елемент ListView
    countriesList = (ListView) findViewById(R.id.countriesList);

    // створюємо адаптер
    ArrayAdapter < String > adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_multiple_choice, countries);

    // встановлюємо для переліку адаптер 
    countriesList.setAdapter(adapter);

    // Додаємо для списку слухач 
    countriesList.setOnItemClickListener(new OnItemClickListener() {

      @Override

      public void onItemClick(AdapterView < ? > parent, View v, int position, long id)

      {

        SparseBooleanArray sp = countriesList.getCheckedItemPositions();

        String selectedItems = "";

        for (int i = 0; i < countries.length; i++)

        {

          if (sp.get(i))

            selectedItems += countries[i] + ",";

        }

        // встановлення тексту елемента 
        TextView selection.setText("Вибрано:" + selectedItems);

      }

    });

  }

}

Ресурс android.R.layout.simple_list_item_multiple_choice представляє стандартну розмітку, яка надається фреймворком, для створення списку з

множинним вибором.

А при виборі елементів ми отримуємо всі вибрані позиції в об'єкт SparseBooleanArray, потім проходимо по всьому масиву, і якщо позиція елемента в

масиві є в SparseBooleanArray, тобто вона обрана, то додаємо зазначений елемент в рядок.

 

Додавання і видалення в ListView

Після прив'язки ListView до джерела даних через адаптер ми можемо працювати з даними - додавати, видаляти, змінювати тільки через адаптер.

ListView служить тільки для відображення даних.

Для управління даними ми можемо використовувати методи адаптера або безпосередньо джерела даних. Наприклад, за допомогою методу add () класу

ArrayAdapter можна додати новий елемент в кінець масиву-джерела даних. Метод insert () дозволяє додати нове значення за певним індексом, а метод

remove () дозволяє видалити об'єкт з масиву. За допомогою методу sort () можна провести сортування масиву

Однак після застосування вищевказаних методів зміни торкнуться тільки масиву, який виступає джерелом даних. Щоб синхронізувати зміни з елементом

ListView, треба викликати у адаптера метод notifyDataSetChanged ().

Наприклад, визначимо в файлі activity_main.xml наступні елементи:

<?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="match_parent"
android:orientation="vertical" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<EditText
android:id="@+id/phone"
android:layout_weight="4"
android:layout_width="0dp"
android:layout_height="wrap_content" />

<Button
android:layout_weight="1"
android:text="+"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:onClick="add"/>

<Button
android:layout_weight="1"
android:text="-" android:layout_width="0dp"
android:layout_height="wrap_content"
android:onClick="remove"/>

</LinearLayout>

<ListView
android:choiceMode="multipleChoice"
android:id="@+id/phonesList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

</LinearLayout>

Для виведення списку призначений ListView з можливістю множинного вибору елементів. Для додавання і видалення визначені дві кнопки. Для

введення нового об'єкта в список призначене поле EditText.

Тепер змінимо клас MainActivity:

package com.example.listapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;

import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

  ArrayList < String > phones = новий ArrayList();
  ArrayAdapter < String > adapter;

  ArrayList < String > selectedPhones = новий ArrayList();
  ListView phonesList;

  @Override

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

    phones.add("iPhone 7");
    phones.add("Samsung Galaxy S7");
    phones.add("Google Pixel");
    phones.add("Huawei P10");

    phones.add("HP Elite z3");

    phonesList = (ListView) findViewById(R.id.phonesList);

    adapter = new ArrayAdapter < String > (this, android.R.layout.simple_list_item_multiple_choice, phones);

    phonesList.setAdapter(adapter);

    // обробка установки та зняття позначки у списку
    phonesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {

      @Override

      public void onItemClick(AdapterView < ? > parent, View v, int position, long id)

      {

        // Отримуємо натиснутий елемент
        String phone = adapter.getItem(position);
        if (phonesList.isItemChecked(position) == true) {

          selectedPhones.add(phone);

        } else {

          selectedPhones.remove(phone);

        }

      }

    });

  }

  public void add(View view) {

    EditText phoneEditText = (EditText) findViewById(R.id.phone);
    String phone = phoneEditText.getText().toString();
    if (!phone.isEmpty() && phones.contains(phone) == false) {

      adapter.add(phone);
      phoneEditText.setText("");
      adapter.notifyDataSetChanged();

    }

  }

  public void remove(View view) {

    // отримуємо та видаляємо виділені елементи 
    for (int i = 0; i < selectedPhones.size(); i++) {

      adapter.remove(selectedPhones.get(i));

    }

    // знімаємо всі раніше встановлені позначки 
    phonesList.clearChoices();

    // Очищаємо масив вибраних об'єктів 
    selectedPhones.clear();

    adapter.notifyDataSetChanged();

  }

}

З додаванням все відносно просто: отримуємо введений рядок і додаємо в список за допомогою методу adapter.add (). Щоб оновити ListView після

додавання викликається метод adapter.notifyDataSetChanged ().

А для видалення створюється додатковий список selectedPhones, який буде містити виділені елементи. Для отримання виділених елементів і додавання

їх в список використовується слухач AdapterView.OnItemClickListener, метод onItemClick () якого викликається при встановленні або знятті позначки з

елемента, тобто при будь-якому натисканні на елемент.

При натисканні на кнопку видалення проходимо по списку виділених елементів і викликаємо для кожного з них метод adapter.remove ().

 

Розширення списків і створення адаптера

Традиційні списки ListView, що використовують стандартні адаптери ArrayAdapter, прекрасно працюють з масивами рядків. Однак частіше ми будемо

стикатися з більш складними за структурою списками, де один елемент представляє не один рядок, а кілька рядків, картинок і інших компонентів.

Для створення складного списку нам треба перевизначити один з використовуваних адаптерів.

Оскільки, як правило, використовується ArrayAdapter, то саме його ми і перевизначимо.

Але спочатку визначимо модель, дані якої будуть відображатися в списку. Для цього додамо в той же каталог, де знаходиться клас MainActivity, новий

клас. Для цього натиснемо на даний каталог правою кнопкою миші і в меню виберемо New -> Java Class:

 

У вікні вкажемо ім'я класу State.

Після додавання змінимо клас State наступним чином:

public class State {

  private String name; // Назва 
  private String capital; // столиця

  private int flagResource; // ресурс прапора

  Public State(String name, String capital, int flag) {

    this.name = name;
    this.capital = capital;
    this.flagResource = flag;

  }

  public String getName() {
    return this.name;

  }

  public void setName(String name) {
    this.name = name;

  }

  public String getCapital() {
    return this.capital;

  }

  public void setCapital(String capital) {
    this.capital = capital;

  }

  public int getFlagResource() {
    return this.flagResource;

  }

  public void setFlagResource(int flagResource) {
    this.flagResource = flagResource;

  }

}

Даний клас зберігає два строкових поля - назву держави і її столицю, а також числове поле, яке буде вказувати на ресурс зображення з папки drawable,

яке буде відображати прапор держави.

Далі додамо в папку res / layout новий файл list_item.xml, який буде представляти розмітку одного елемента в списку:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">

<ImageView
android:id="@+id/flag"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_width="70dp"
android:layout_height="50dp" />

<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Назва" />

<TextView
android:id="@+id/capital"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Столиця" />

</LinearLayout>

</LinearLayout>

Кожен елемент матиме зображення у вигляді ImageView і два компонента TextView для відображення назви і столиці держави.

Після цього додамо в каталог, де знаходяться класи MainActivity і State, новий клас, який назвемо StateAdapter:

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 StateAdapter extends ArrayAdapter < State > {
  private LayoutInflater inflater;

  private int layout;

  private List < State > states;

  public StateAdapter(Context context, int resource, List < State > states) {
    super(context, resource, states);

    this.states = states;
    this.layout = resource;

    this.inflater = LayoutInflater.from(context);

  }

  public View getView(int position, View convertView, ViewGroup parent) {
    View view = inflater.inflate(this.layout, parent, false);

    ImageView flagView = (ImageView) view.findViewById(R.id.flag);
    TextView nameView = (TextView) view.findViewById(R.id.name);
    TextView capitalView = (TextView) view.findViewById(R.id.capital);

    State state = states.get(position);
    flagView.setImageResource(state.getFlagResource());

    nameView.setText(state.getName());
    capitalView.setText(state.getCapital());

    return view;

  }

}

Вся взаємодія зі списком тут буде йти через клас StateAdapter. У конструкторі StateAdapter нам треба передати в конструктор базового класу три

параметра:

У конструкторі StateAdapter ми отримуємо ресурс розмітки і набір об'єктів і зберігаємо їх в окремій змінній. Крім того, для створення об'єкта View за

отриманим ресурсом розмітки потрібен об'єкт LayoutInflater, який також зберігається в змінну.

У методі getView () встановлюється відображення елемента списку. Даний метод приймає три параметри:

В даному випадку за допомогою об'єкта LayoutInflater створюємо об'єкт View для кожного окремого елемента в списку:

View view=inflater.inflate(this.layout, parent, false);

З створеного об'єкта View отримуємо елементи ImageView і TextView по id:

ImageView flagView = (ImageView) view.findViewById(R.id.flag);

TextView nameView = (TextView) view.findViewById(R.id.name);

TextView capitalView = (TextView) view.findViewById(R.id.capital);

Це ті елементи, які визначені в файлі list_item.xml. Тут же ми їх отримуємо.

Далі використовуючи параметр position, отримуємо об'єкт State, для якого створюється розмітка:

State state = states.get (position);

Потім отримані елементи ImageView і TextView наповнюємо з отриманого по позиції об'єкта State:

flagView.setImageResource (state.getFlagResource ());

nameView.setText (state.getName ());

capitalView.setText (state.getCapital ());

І в кінці створений для відображення об'єкта State елемент View повертається з методу:

return view;

Для використання зображень додамо в папку res / drawable кілька зображень, в моєму випадку це п'ять зображень прапорів держав. В результаті проект

буде виглядати наступним чином:

 

У файлі activity_main.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="wrap_content"
android:orientation="horizontal">

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

</LinearLayout>

А в файлі MainActivity з'єднаємо StateAdapter з ListView:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;

import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
  private List < State > states = new ArrayList();
  ListView countriesList;

  @Override

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

    // Початкова ініціалізація списку 
    setInitialData();

    // Отримуємо елемент ListView
    countriesList = (ListView) findViewById(R.id.countriesList);

    // створюємо адаптер
    StateAdapter stateAdapter = новий StateAdapter(this, R.layout.list_item, states);

    // встановлюємо адаптер 
    countriesList.setAdapter(stateAdapter);

    // слухач вибору списку
    AdapterView.OnItemClickListener itemListener = новий AdapterView.OnItemClickListener() {
      @Override

      public void onItemClick(AdapterView < ? > parent, View v, int position, long id) {

        // Отримуємо обраний пункт
        State selectedState = (State) parent.getItemAtPosition(position);
        Toast.makeText(getApplicationContext(), "Було обрано пункт " +

          selectedState.getName(),

          Toast.LENGTH_SHORT).show();

      }

    };

    countriesList.setOnItemClickListener(itemListener);

  }

  private void setInitialData() {

    states.add(new State("Бразилія", "Бразилія", R.drawable.brazilia));
    states.add(new State("Аргентина", "Буенос-Айрес", R.drawable.argentina));
    states.add(new State("Колумбія", "Богота", R.drawable.columbia));
    states.add(new State("Уругвай", "Монтевідео", R.drawable.uruguai));
    states.add(new State("Чилі", "Сантьяго", R.drawable.chile));

  }

}

Як джерело даних тут виступає клас ArrayList, який отримує дані в методі setInitialData. Кожному елементу State в списку передається назва держави,

його столиця і ресурс зображення з папки res / drawable, який представляє прапор держави.

При створенні адаптера йому передається раніше створений ресурс розмітки list_item.xml і список states:

StateAdapter stateAdapter = new StateAdapter(this, R.layout.list_item, states);

 

Оптимізація адаптера і View Holder

У минулій темі був створений кастомний адаптер, який дозволяв працювати зі складними списками об'єктів:

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 StateAdapter extends ArrayAdapter < State > {
  private LayoutInflater inflater;

  private int layout;

  private List < State > states;

  public StateAdapter(Context context, int resource, List < State > states) {
    super(context, resource, states);

    this.states = states;
    this.layout = resource;

    this.inflater = LayoutInflater.from(context);

  }

  public View getView(int position, View convertView, ViewGroup parent) {
    View view = inflater.inflate(this.layout, parent, false);

    ImageView flagView = (ImageView) view.findViewById(R.id.flag);
    TextView nameView = (TextView) view.findViewById(R.id.name);
    TextView capitalView = (TextView) view.findViewById(R.id.capital);

    State state = states.get(position);

    flagView.setImageResource(state.getFlagResource());
    nameView.setText(state.getName());
    capitalView.setText(state.getCapital());

    return view;

  }

}

Але цей адаптер має один дуже великий мінус - при прокручуванні в ListView, якщо в списку дуже багато об'єктів, то для кожного елемента, коли він

потрапить в зону видимості, буде повторно викликатися метод getView, в якому буде заново створюватися новий об'єкт View. Відповідно буде

збільшуватися використання пам'яті і знижуватися продуктивність. Тому оптимізуємо код методу getView:

Public View getView(int position, View convertView, ViewGroup parent) {

  if (convertView == null) {

    convertView = inflater.inflate(this.layout, parent, false);

  }

  ImageView flagView = (ImageView) convertView.findViewById(R.id.flag);
  TextView nameView = (TextView) convertView.findViewById(R.id.name);
  TextView capitalView = (TextView) convertView.findViewById(R.id.capital);

  State state = states.get(position);

  flagView.setImageResource(state.getFlagResource());
  nameView.setText(state.getName());
  capitalView.setText(state.getCapital());

  return convertView;

}

Параметр convertView вказує на елемент View, який використовується для об'єкта в списку по позиції position. Якщо раніше вже створювався View для

цього об'єкта, то параметр convertView вже містить деяке значення, яке ми можемо використовувати.

У цьому випадку ми будемо повторно використовувати вже створені об'єкти і збільшимо продуктивність, однак цей код можна ще більше оптимізувати.

Справа в тому, що отримання елементів з id теж відносно витратна операція. Тому далі оптимізуємо код StateAdapter, змінивши його наступним чином:

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 StateAdapter extends ArrayAdapter < State > {
  private LayoutInflater inflater;

  private int layout;private List < State > states;

  public StateAdapter(Context context, int resource, List < State > states) {
    super(context, resource, states);

    this.states = states;
    this.layout = resource;

    this.inflater = LayoutInflater.from(context);

  }

  Public View getView(int position, View convertView, ViewGroup parent) {

    ViewHolder viewHolder;
    if (convertView == null) {

      convertView = inflater.inflate(this.layout, parent, false);
      viewHolder = новий ViewHolder(convertView);
      convertView.setTag(viewHolder);

    } else {

      viewHolder = (ViewHolder) convertView.getTag();

    }

    State state = states.get(position);

    viewHolder.imageView.setImageResource(state.getFlagResource());
    viewHolder.nameView.setText(state.getName());
    viewHolder.capitalView.setText(state.getCapital());

    return convertView;

  }

  private class ViewHolder {
    final ImageView imageView;

    final TextView nameView, capitalView;
    ViewHolder(View view) {

      imageView = (ImageView) view.findViewById(R.id.flag);
      nameView = (TextView) view.findViewById(R.id.name);
      capitalView = (TextView) view.findViewById(R.id.capital);

    }

  }

}

Для зберігання посилань на використовувані елементи ImageView і TextView визначено внутрішній приватний клас ViewHolder, який в конструкторі

отримує об'єкт View, що містить ImageView і TextView.

У методі getView, якщо convertView дорівнює null (тобто якщо раніше для об'єкта не створена розмітка) створюємо об'єкт ViewHolder, який зберігаємо в

тег в convertView:

convertView.setTag (viewHolder);

Якщо ж розмітка для об'єкта в ListView вже раніше була створена, то назад отримуємо ViewHolder з тега:

viewHolder = (ViewHolder) convertView.getTag ();

Потім також для ImageView і TextView у ViewHolder встановлюються значення з об'єкта State:

viewHolder.imageView.setImageResource (state.getFlagResource ());

viewHolder.nameView.setText (state.getName ());

viewHolder.capitalView.setText (state.getCapital ());

І тепер ListView особливо при великих списках буде працювати плавніше і продуктивніше, ніж в минулій темі:

 

Складний список з кнопками

Раніше були розглянуті кастомні адаптери, які дозволяють виводити в списки складні дані. Тепер підемо далі і розглянемо, як ми можемо додати в

списки інші елементи, наприклад, кнопки, і обробляти їх події.

Для цього спочатку визначимо наступний клас Product:

package com.example.listapp;

public class Product {
  private String name;
  private int count;
  private String unit;

  Product(String name, String unit) {
    this.name = name;
    this.count = 0;

    this.unit = unit;

  }

  public String getUnit() {
    return this.unit;

  }

  public void setCount(int count) {
    this.count = count;

  }

  public int getCount() {
    return count;

  }

  public void setName(String name) {
    this.name = name;

  }

  public String getName() {
    return this.name;

  }

}

Даний клас зберігає назву, кількість продукту, а також одиницю виміру. І об'єкти цього класу будемо виводити в список.

Для цього в папку res / layout додамо новий файл list_item.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
ndroid:orientation="horizontal"
android:padding="16dp">

<TextView
android:id="@+id/nameView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textSize="18sp" />

<TextView
android:id="@+id/countView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textSize="18sp" />

<Button
android:id="@+id/addButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="+"/>

<Button
android:id="@+id/removeButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="-"/>

</LinearLayout>

Тут визначені два текстових поля для виведення назви і кількості продукту і дві кнопки для додавання і видалення однієї одиниці продукту.

Тепер додамо клас адаптера, який назвемо ProductAdapter:

package com.example.listapp;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;

import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.TextView;

import java.util.ArrayList;

class ProductAdapter extends ArrayAdapter < Product > {
  private LayoutInflater inflater;

  private int layout;

  private ArrayList < Product > productList;

  ProductAdapter(Context context, int resource, ArrayList < Product > products) {
    super(context, resource, products);

    this.productList = products;
    this.layout = resource;

    this.inflater = LayoutInflater.from(context);

  }

  Public View getView(int position, View convertView, ViewGroup parent) {

    final ViewHolder viewHolder;
    if (convertView == null) {

      convertView = inflater.inflate(this.layout, parent, false);
      viewHolder = новий ViewHolder(convertView);
      convertView.setTag(viewHolder);

    } else {

      viewHolder = (ViewHolder) convertView.getTag();

    }

    final Product product = productList.get(position);

    viewHolder.nameView.setText(product.getName());
    viewHolder.countView.setText(formatValue(product.getCount(), product.getUnit()));

    viewHolder.removeButton.setOnClickListener(new View.OnClickListener() {
      @Override

      public void onClick(View v) {

        int count = product.getCount() - 1;
        if (count < 0) count = 0;
        product.setCount(count);

        viewHolder.countView.setText(formatValue(count, product.getUnit()));

      }

    });

    viewHolder.addButton.setOnClickListener(new View.OnClickListener() {
      @Override

      public void onClick(View v) {

        int count = product.getCount() + 1;
        product.setCount(count);

        viewHolder.countView.setText(formatValue(count, product.getUnit()));

      }

    });

    return convertView;

  }

  private String formatValue(int count, String unit) {
    return String.valueOf(count) + " " + unit;

  }

  private class ViewHolder {

    final Button addButton, removeButton;
    final TextView nameView, countView;
    ViewHolder(View view) {

      addButton = (Button) view.findViewById(R.id.addButton);
      removeButton = (Button) view.findViewById(R.id.removeButton);
      nameView = (TextView) view.findViewById(R.id.nameView);
      countView = (TextView) view.findViewById(R.id.countView);

    }

  }

}

Для кожної кнопки тут визначено обробник натискання, в якому ми зменшуємо, або збільшуємо кількість продукту на одиницю і потім вставляємо текст у

відповідне текстове поле.

Далі в файлі activity_main.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="match_parent">

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

</LinearLayout>

І змінимо клас MainActivity:

package com.example.listapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.widget.ListView;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
  ArrayList < Product > products = new ArrayList();

  ListView productList;
  @Override

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

    if (products.size() == 0) {

      products.add(new Product("Картопля", "кг."));
      products.add(new Product("Чай", "шт."));

      products.add(new Product("Яйця", "шт."));
      products.add(new Product("Молоко", "л."));
      products.add(new Product("Макарони", "кг."));

    }

    productList = (ListView) findViewById(R.id.productList);

    ProductAdapter adapter = новий ProductAdapter(this, R.layout.list_item, products);
    productList.setAdapter(adapter);

  }

}

У підсумку вийде наступний проект:

 

І після запуску програми ми зможемо управляти кількістю продуктів через кнопки:

ListActivity

Для спрощення доступу до елементів списку використовується клас ListActivity. ListActivity є класом, успадкованим від Activity і розроблений спеціально

для роботи зі списками.

Отже, подивимося на прикладі. По-перше, визначимо в файлі розмітки activity_main.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="wrap_content"
android:orientation="vertical">

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

</LinearLayout>

Зверніть увагу на оголошення ідентифікатора ListView:

android: id = "@ android: id / list".

Подібне оголошення обов'язкове, щоб ListActivity розпізнала список і могла б їм управляти.

Крім ListView в файлі розмітки інтерфейсу також можуть бути і інші елементи. Але в даному випадку обмежимося лише елементом ListView.

Далі змінимо код класу MainActivity:

import android.app.ListActivity;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;

import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Toast;

public class MainActivity extends ListActivity {

  String[] countries = {
    "Бразилія",
    "Аргентина",
    "Колумбія",
    "Чилі",
    "Уругвай"
  };
  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // створюємо адаптер
    ArrayAdapter < String > adapter = new ArrayAdapter < String > (this, android.R.layout.simple_list_item_1, countries);

    setListAdapter(adapter);

    AdapterView.OnItemClickListener itemListener = новий AdapterView.OnItemClickListener() {
      @Override

      public void onItemClick(AdapterView < ? > parent, View v, int position, long id) {

        Toast.makeText(getApplicationContext(), "Було обрано пункт " + parent.getItemAtPosition(position).toString(),

          Toast.LENGTH_SHORT).show();

      }

    };

    getListView().setOnItemClickListener(itemListener);

  }

}

Тепер клас MainActivity розширює базовий клас ListActivity.

Тут як і в випадку з ListView ми створюємо адаптер ArrayAdapter, тільки встановлюємо його через метод setListAdapter, який визначений у ListActivity.

Далі створюється об'єкт слухача OnItemClickListener, який буде обробляти вибір елементів списку.

В кінці ми використовуємо метод getListView (), який повертає об'єкт ListView. І потім встановлюємо для нього визначений вище слухач.

 

Выпадающий список Spinner

Spinner являє собою список, що випадає. Визначимо в файлі розмітки activity_main.xml елемент Spinner:

<?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="match_parent"
android:orientation="vertical" >

<Spinner
android:id="@+id/cities"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

</LinearLayout>

Як джерело даних, як і для ListView, так і для Spinner може служити простий список або масив, створений програмно, або ресурс string-array. Взаємодія

з джерелом даних також буде йти через адаптер. В даному випадку визначимо джерело програмно у вигляді масиву в коді MainActivity:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.widget.ArrayAdapter;
import android.widget.Spinner;

public class MainActivity extends AppCompatActivity {

  String[] cities = {
    "Токіо",
    "Київ",
    "Мельбурн",
    "Братислава",
    "Мальмьо",
    "Варшава"
  };
  @Override

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

    spinner

    Spinner spinner = (Spinner) findViewById(R.id.cities);

    // Створюємо адаптер ArrayAdapter за допомогою масиву рядків та стандартної розмітки елемету
    ArrayAdapter < String > adapter = new ArrayAdapter < String > (this,

      android.R.layout.simple_spinner_item, cities);

    // Визначаємо розмітку для використання при виборі елемента 
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

    // Застосовуємо адаптер елемента spinner 
    spinner.setAdapter(adapter);

  }

}

Використовуваний при створенні ArrayAdapter ресурс android.R.layout.simple_spinner_item надається платформою і є стандартною розміткою для

створення списку.

За допомогою метода adapter.setDropDownViewResource (android.R.layout.simple_spinner_dropdown_item) встановлюються додаткові візуальні

можливості списку. А передається в метод ресурс android.R.layout.simple_spinner_dropdown_item, що використовується для візуалізації списку і також

надається платформою.

 

Обработка выбора элемента

Використовуючи слухач OnItemSelectedListener, зокрема його метод onItemSelected (), ми можемо обробляти обраний елемент зі списку. Спочатку

додамо в розмітку інтерфейсу текстове поле, яке буде виводити обраний елемент:

<?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="match_parent"
android:orientation="vertical" >

<TextView
android:id="@+id/selection"
android:layout_width="match_parent"
android:layout_height="wrap_content">

</TextView>

<Spinner
android:id="@+id/cities"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

</LinearLayout>

І змінимо код MainActivity, визначивши для елемента Spinner слухач OnItemSelectedListener:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;

import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

  String[] cities = {
    "Токіо",
    "Київ",
    "Мельбурн",
    "Братислава",
    "Мальмьо",
    "Варшава"
  };
  TextView selection;

  @Override

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

    selection = (TextView) findViewById(R.id.selection);

    spinner

    Spinner spinner = (Spinner) findViewById(R.id.cities);

    // Створюємо адаптер ArrayAdapter за допомогою масиву рядків та стандартної розмітки елемету
    ArrayAdapter < String > adapter = new ArrayAdapter < String > (this,

      android.R.layout.simple_spinner_item, cities);

    // Визначаємо розмітку для використання при виборі елемента 
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

    // Застосовуємо адаптер елемента spinner 
    spinner.setAdapter(adapter);

    OnItemSelectedListener itemSelectedListener = New OnItemSelectedListener() {
      @Override

      public void onItemSelected(AdapterView < ? > parent, View view, int position, long id) {

        // Отримуємо обраний об'єкт
        String item = (String) parent.getItemAtPosition(position);
        selection.setText(item);

      }

      @Override

      public void onNothingSelected(AdapterView < ? > parent) {

      }

    };

    spinner.setOnItemSelectedListener(itemSelectedListener);

  }

}

Метод onItemSelected отримує чотири параметри:

String item = (String) parent.getItemAtPosition (position);

 

Віджет автодоповнення AutoCompleteTextView

AutoCompleteTextView представляє елемент, створений на основі класу EditText і володіє можливістю авто доповнення.

По-перше, оголосимо в ресурсі розмітки даний елемент:

<?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="match_parent"
android:orientation="vertical" >

<AutoCompleteTextView
android:id="@+id/autocomplete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionHint="Введіть місто"
android:completionThreshold="1"/>

</LinearLayout>

Атрибут android: completionHint дозволяє задати напис, котрий відображається внизу списку, а властивість android: completionThreshold встановлює

скільки символів треба ввести, щоб почало працювати автодоповнення. Тобто в даному випадку, вже після введення одного символу повинен з’являтися

список з підстановками.

Як і у випадку з елементами ListView і Spinner, AutoCompleteTextView підключається до джерела даних через адаптер. Джерелом даних знову ж може

служити масив або список об'єктів, або ресурс string-array.

Тепер підключимо до віджету масив рядків в класі MainActivity:

package com.example.listapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.widget.ArrayAdapter;

import android.widget.AutoCompleteTextView;

public class MainActivity extends AppCompatActivity {

  String[] cities = {
    "Токіо",
    "Київ",
    "Мельбурн",
    "Братислава",
    "Мальмьо",
    "Варшава"
  };
  @Override

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

    // Отримуємо посилання на елемент AutoCompleteTextView у розмітці 
    AutoCompleteTextView autoCompleteTextView = (AutoCompleteTextView)

    findViewById(R.id.autocomplete);

    // Створюємо адаптер для автозаповнення елемента 
    AutoCompleteTextView ArrayAdapter < String > adapter =
      new ArrayAdapter < String > (this, R.layout.support_simple_spinner_dropdown_item, cities);

    autoCompleteTextView.setAdapter(adapter);

  }

}

Після введення в текстове поле однієї літери відобразиться список з варіантами автодоповнення, де можна вибрати кращий:

MultiAutoCompleteTextView

Цей віджет доповнює функціональність елемента AutoCompleteTextView. MultiAutoCompleteTextView дозволяє використовувати автодоповнення не

тільки для одного рядка, але і для окремих слів. Наприклад, якщо вводиться слово і після нього ставиться кома, то автозаповнення все одно буде

працювати для нововведених слів після коми або іншого роздільника.

MultiAutoCompleteTextView має таку ж форму оголошення, як і AutoCompleteTextView:

<?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="match_parent"
android:orientation="vertical" >

<MultiAutoCompleteTextView
android:id="@+id/autocomplete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionHint="Введіть місто"
android:completionThreshold="1"/>

</LinearLayout>

Щоб включити MultiAutoCompleteTextView в коді, треба встановити токен роздільника:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.widget.ArrayAdapter;

import android.widget.MultiAutoCompleteTextView;
public class MainActivity extends AppCompatActivity {

  String[] cities = {
    "Токіо",
    "Київ",
    "Мельбурн",
    "Братислава",
    "Мальмьо",
    "Варшава"
  };
  @Override

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

    // Отримуємо посилання на елемент AutoCompleteTextView у розмітці
    MultiAutoCompleteTextView autoCompleteTextView = (MultiAutoCompleteTextView)

    findViewById(R.id.autocomplete);

    // Створюємо адаптер для автозаповнення елемента
    AutoCompleteTextView ArrayAdapter < String > adapter = new ArrayAdapter < String > (this,

      R.layout.support_simple_spinner_dropdown_item, cities);
    autoCompleteTextView.setAdapter(adapter);

    // Установка коми як роздільник
    autoCompleteTextView.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());

  }

}

Тут як роздільник використовується вбудований роздільник на основі коми CommaTokenizer (). При необхідності ми можемо створити свої роздільники.

GridView

Елемент GridView представляє відображення у вигляді таблиці - набору рядків і стовпців.

Створимо розмітку GridView:

<?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="match_parent"
android:orientation="vertical" >

<GridView
android:id="@+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="2"
android:verticalSpacing="16dp"
android:horizontalSpacing="16dp"
android:stretchMode="columnWidth" />

</LinearLayout>

За допомогою атрибута android: numColumns можна налаштувати кількість стовпців в гріді. Тепер, як і в випадку з ListView, треба встановити зв'язок з адаптером:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;

import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

  String[] countries = {
    "Бразилія",
    "Аргентина",
    "Чилі",
    "Колумбія",
    "Уругвай",
    "Парагвай"
  };
  @Override

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

    // отримуємо елемент GridView
    GridView countriesList = (GridView) findViewById(R.id.gridview);

    // створюємо адаптер
    ArrayAdapter < String > adapter = new ArrayAdapter < String > (this, android.R.layout.simple_list_item_1, countries);

    countriesList.setAdapter(adapter);

    AdapterView.OnItemClickListener itemListener = новий AdapterView.OnItemClickListener() {
      @Override

      public void onItemClick(AdapterView < ? > parent, View v,

        int position, long id) {
        Toast.makeText(getApplicationContext(), "Ви вибрали "

          +
          parent.getItemAtPosition(position).toString(), Toast.LENGTH_SHORT).show();

      }

    };

    countriesList.setOnItemClickListener(itemListener);

  }

}

Для обробки натискання в GridView застосовується слухач AdapterView.OnItemClickListener.

RecyclerView

Елемент RecyclerView призначений для оптимізації роботи зі списками і багато в чому дозволяє підвищити продуктивність в порівнянні зі стандартним

ListView.

За замовчуванням функціональність RecyclerView недоступна в Android Studio, тому її треба вручну додавати. Для цього перейдемо до файлу

build.gradle, котрий розташований в проекті всередині папки app і який визначає конфігурацію модуля app:

 

Змінимо в ньому вузол Dependencies наступним чином:

dependencies {

  compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {

    exclude group: 'com.android.support',
    module: 'support-annotations'

  })

  compile 'com.android.support:appcompat-v7:25.3.0'
  compile 'com.android.support:recyclerview-v7:25.3.0'

  compile 'com.android.support.constraint:constraint-layout:1.0.2'
  testCompile 'junit:junit:4.12'

}

В даному випадку додано визначення пакету "com.android.support:recyclerview-v7:25.3.0". Для синхронізації файлу build.gradle з проектом натиснемо на

посилання Sync Now:

 

Для представлення даних додамо в проект клас Phone:

package com.example.recycleviewapp;
public class Phone {

  private String name;
  private String company;
  private int image;

  Public Phone(String name, String company, int image) {

    this.name = name;
    this.company = company;
    this.image = image;

  }

  public String getName() {
    return this.name;

  }

  public void setName(String name) {
    this.name = name;

  }

  public String getCompany() {
    return this.company;

  }

  public void setCompany(String company) {
    this.company = company;

  }

  public int getImage() {
    return this.image;

  }

  public void setImage(int image) {
    this.image = image;

  }

}

Клас Phone містить поля для зберігання моделі і виробника телефону, а також посиланнями на ресурс зображення.

Припустимо, ми хочемо вивести список об'єктів Phone на екрані смартфона за допомогою RecyclerView. Для цього додамо в папку res / layout новий

файл list_item.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">

<ImageView
android:id="@+id/image"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_width="100dp"
android:layout_height="100dp" />

<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/company"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

</LinearLayout>

Цей файл визначає розмітку для виведення одного об'єкта Phone.

Як і у випадку з ListView, для виведення складних об'єктів в RecyclerView необхідно визначити свій адаптер. Тому додамо новий клас DataAdapter:

package com.example.recycleviewapp;

import android.content.Context;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;

import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;

class DataAdapter extends RecyclerView.Adapter < DataAdapter.ViewHolder > {
  private LayoutInflater inflater;

  private List < Phone > phones;

  DataAdapter(Context context, List < phone > phones) {
    this.phones = phones;

    this.inflater = LayoutInflater.from(context);

  }

  @Override

  public DataAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = inflater.inflate(R.layout.list_item, parent, false);
    return new ViewHolder(view);

  }

  @Override

  public void onBindViewHolder(DataAdapter.ViewHolder holder, int position) {
    Phone phone = phones.get(position);
    holder.imageView.setImageResource(phone.getImage());

    holder.nameView.setText(phone.getName());
    holder.companyView.setText(phone.getCompany());

  }

  @Override

  public int getItemCount() {
    return phones.size();

  }

  public class ViewHolder extends RecyclerView.ViewHolder {
    final ImageView imageView;

    final TextView nameView, companyView;
    ViewHolder(View view) {

      super(view);

      imageView = (ImageView) view.findViewById(R.id.image);
      nameView = (TextView) view.findViewById(R.id.name);
      companyView = (TextView) view.findViewById(R.id.company);

    }

  }

}

Адаптер, який використовується в RecyclerView, повинен успадковуватися від абстрактного класу RecyclerView.Adapter. Цей клас визначає три методи:

Для зберігання даних в класі адаптера визначено статичний клас ViewHolder, який використовує певні в list_item.xml елементи управління.

Тепер визначимо в файлі activity_main.xml елемент RecyclerView:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
android:id="@+id/list"
app:layoutManager="LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</android.support.constraint.ConstraintLayout>

Для RecyclerView слід встановити атрибут app: layoutManager, який вказує на тип менеджера компоновки.

І в кінці змінимо клас MainActivity:

package com.example.recycleviewapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;

import java.util.List;

public class MainActivity extends AppCompatActivity {

  List < Phone > phones = new ArrayList < > ();
  @Override

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

    setInitialData();

    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);

    // створюємо адаптер
    DataAdapter adapter = new DataAdapter(this, phones);

    // встановлюємо для переліку адаптер 
    recyclerView.setAdapter(adapter);

  }

  private void setInitialData() {

    phones.add(new Phone("Huawei P10", "Huawei", R.drawable.mate8));
    phones.add(new Phone("Elite z3", "HP", R.drawable.lumia950));
    phones.add(new Phone("Galaxy S8", "Samsung", R.drawable.galaxys6));
    phones.add(new Phone("LG G 5", "LG", R.drawable.nexus5x));

  }

}

За допомогою методу setInitialData () встановлюється набір початкових даних. В даному випадку мається на увазі, що в папці res / drawables

знаходиться ряд ресурсів зображень для об'єктів Phone.

Як і у випадку з виведенням списку через ListView тут спочатку отримуємо елемент RecyclerView, створюємо адаптер і встановлюємо адаптер для

RecyclerView. Весь проект в підсумку буде виглядати наступним чином:

 

В результате RecyclerView выведет набор объектов:

 


© 2006—2023 СумДУ