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

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

Діалогові вікна

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


Діалогові вікна

DatePickerDialog і TimePickerDialog

За замовчуванням в Android вже визначені два діалогових вікна - DatePickerDialog і TimePickerDialog, які дозволяють вибрати дату і час.

Крім дати DatePickerDialog дозволяє обробити вибір дати за допомогою слухачів OnDateChangedListener і OnDateSetListener. Що дозволяє

використовувати обрану дату далі в додатку.

Подібним чином TimePickerDialog дозволяє обробити вибір часу за допомогою слухачів OnTimeChangedListener і OnTimeSetListener

При роботі з даними компонентами треба враховувати, що відлік місяців в DatePickerDialog починається з нуля, тобто січень буде мати номер 0, а

грудень - 11. І аналогічно в TimePickerDialog відлік секунд і хвилин буде йти з 0 до 59, а годин - з 0 до 23.

Використовуємо DatePickerDialog і TimePickerDialog в додатку. Визначимо наступну розмітку інтерфейсу в activity_main.xml:

<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/currentDateTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp" />

<Button
android:id="@+id/timeButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Змінити час"
android:onClick="setTime" />

<Button
android:id="@+id/dateButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Змінити дату"
android:onClick="setDate" />

</LinearLayout>

Тут визначені дві кнопки для вибору дати і часу і текстове поле, яке відображає вибрані дату і час. І змінимо код MainActivity:

package com.example.dialogsapp;

import android.app.DatePickerDialog;
import android.app.TimePickerDialog;

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

import android.text.format.DateUtils;
import android.view.View;

import android.widget.DatePicker;
import android.widget.TextView;
import android.widget.TimePicker;

import java.util.Calendar;

public class MainActivity extends AppCompatActivity {

  TextView currentDateTime;

  Calendar dateAndTime = Calendar.getInstance();
  @Override

  public void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstance);
    setContentView(R.layout.activity_main);
    currentDateTime = (TextView) findViewById(R.id.currentDateTime);
    setInitialDateTime();

  }

  // Відображаємо діалогове вікно для вибору дати 
  public void setDate(View v) {

    New DatePickerDialog(MainActivity.this, d, dateAndTime.get(Calendar.YEAR), dateAndTime.get(Calendar.MONTH), dateAndTime.get(Calendar.DAY_OF_MONTH))

      .show();

  }

  // Відображаємо діалогове вікно для вибору часу 
  public void setTime(View v) {

    New TimePickerDialog(MainActivity.this, t, dateAndTime.get(Calendar.HOUR_OF_DAY), dateAndTime.get(Calendar.MINUTE), true)

      .show();

  }

  // встановлення початкових дати та часу 
  private void setInitialDateTime() {

    currentDateTime.setText(DateUtils.formatDateTime(this, dateAndTime.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR

      |
      DateUtils.FORMAT_SHOW_TIME));

  }

  // встановлення обробника вибору часу
  TimePickerDialog.OnTimeSetListener t = new TimePickerDialog.OnTimeSetListener() {
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

      dateAndTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
      dateAndTime.set(Calendar.MINUTE, minute);
      setInitialDateTime();

    }

  };

  // встановлення обробника вибору дати
  DatePickerDialog.OnDateSetListener d = new DatePickerDialog.OnDateSetListener() {

    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
      dateAndTime.set(Calendar.YEAR, year);

      dateAndTime.set(Calendar.MONTH, monthOfYear);
      dateAndTime.set(Calendar.DAY_OF_MONTH, dayOfMonth);
      setInitialDateTime();

    }

  };

}

Ключовим класом тут є java.util.Calendar, який зберігається в стандартній бібліотеці класів Java. У методі setInitialDateTime () ми отримуємо з екземпляра

цього класу кількість мілісекунд dateAndTime.getTimeInMillis () і за допомогою форматування виводимо на текстове поле.

Метод setDate (), що викликається після натискання на кнопку, відображає вікно для вибору дати. При створенні вікна його об'єкту передається

обробник вибору дати DatePickerDialog.OnDateSetListener, який змінює дату у текстовому полі.

Аналогічно метод setTime () відображає вікно для вибору часу. Об'єкт вікна використовує обробник вибору часу TimePickerDialog.OnTimeSetListener,

який змінює час у текстовому полі. І поле запуску, натиснувши на кнопку зміни часу, ми зможемо встановити час:

 

Для установки часу в діалоговому вікні визначена кнопка "Встановити". Аналогічно працює вікно установки дати:

 

DialogFragment і створення діалогових вікон

Для створення своїх діалогових вікон використовується компонент AlertDialog в зв'язці з класом фрагмента DialogFragment. Розглянемо їх застосування.

Спочатку додамо в проект новий клас фрагмента, який назвемо CustomDialogFragment:

package com.example.dialogsapp;

import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;

import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;

public class CustomDialogFragment extends DialogFragment {
  @NonNull

  Public Dialog onCreateDialog(Bundle savedInstanceState) {

    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

    return builder.setTitle("Діалогове вікно").setMessage("Для закриття вікна натисніть

      ОК ").create();

    }

  }

Клас фрагмента містить всю стандартну функціональність фрагмента з його життєвим циклом, але при цьому успадковується від класу DialogFragment,

який додає ряд додаткових функцій. І для його створення ми можемо використати два способи:

Для створення діалогового вікна в методі onCreateDialog () застосовується клас AlertDialog.Builder. За допомогою своїх методів він дозволяє

налаштувати відображення діалогового вікна:

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

Для виклику цього діалогового вікна в файлі activity_main.xml визначимо кнопку:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dialog"
android:onClick="showDialog"/>

</LinearLayout>

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

package com.example.dialogsapp;

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

import android.view.View;

public class MainActivity extends AppCompatActivity {
  @Override

  public void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstance);
    setContentView(R.layout.activity_main);

  }

  public void showDialog(View v) {

    CustomDialogFragment dialog = new CustomDialogFragment();

    dialog.show(getSupportFragmentManager(), "custom");

  }

}

Для виклику діалогового вікна створюється об'єкт фрагмента CustomDialogFragment, потім на ньому викликається метод show (). У цей метод

передається менеджер фрагментів FragmentManager і рядок - довільний тег. І після натискання на кнопку ми зможемо ввести дані в діалогове вікно:

 

Тепер трохи кастомізуємо діалогове вікно:

Public Dialog onCreateDialog(Bundle savedInstanceState) {

  AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

  return builder
    .setTitle("Діалогове вікно")
    .setIcon(android.R.drawable.ic_dialog_alert)
    .setMessage("Для закриття вікна натисніть ОК")
    .setPositiveButton("OK", null)
    .setNegativeButton("Скасування", null)
    .create();

}

 

Тут додається іконка, яка в якості зображення використовує вбудований ресурс

android.R.drawable.ic_dialog_alert і встановлюються дві кнопки. Для кожної кнопки можна встановити текст і обробник натискання. В даному випадку для

обробника натискання передається null, тобто обробник не встановлено.

Тепер додамо в папку res / layout новий файл dialog.xml:

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

<TextView
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello Android 7"/>

</LinearLayout>

І змінимо створення діалогового вікна:

Public Dialog onCreateDialog(Bundle savedInstanceState) {

  AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
  return builder
    .setTitle("Діалогове вікно")
    .setIcon(android.R.drawable.ic_dialog_alert)
    .setView(R.layout.dialog)
    .setPositiveButton("OK", null)
    .setNegativeButton("Скасування", null)
    .create();
}

Метод setView () встановлює в якості інтерфейсу вікна раніше доданий ресурс dialog.xml. При використанні цього методу треба враховувати, що він

доступний починаючи з API 21 (Lollipop). Тому для його застосування може знадобитися змінити мінімальну версію Android у проекті до 21.

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

 

Передача даних в діалогове вікно

Передача даних в діаговое вікно, як і в будь-який фрагмент, здійснюється за допомогою об'єкта Bundle.

Так, визначимо в файлі activity_main.xml список ListView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

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

</LinearLayout>

У класі MainActivity визначимо для цього списку дані:

package com.example.dialogsapp;

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 java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
  @Override

  public void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstance);
    setContentView(R.layout.activity_main);

    ListView phonesList = (ListView) findViewById(R.id.phonesList);
    ArrayList < String > phones = new ArrayList < > ();
    phones.add("Google Pixel");

    phones.add("Huawei P9");
    phones.add("LG G5");
    phones.add("Samsung Galaxy S8");

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

    phonesList.setAdapter(adapter);

    phonesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
      @Override

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

        String selectedPhone = adapter.getItem(position);
        CustomDialogFragment dialog = новий CustomDialogFragment();
        Bundle args = New Bundle();

        args.putString("phone", selectedPhone);
        dialog.setArguments(args);
        dialog.show(getSupportFragmentManager(), "custom");

      }

    });

  }

}

У обробнику натиснення на елемент в списку отримуємо обраний елемент і додаємо його в об'єкт Bundle. Далі через метод dialog.setArguments ()

передаємо дані з Bundle під фрагмент.

Тепер визначимо наступний клас фрагмента CustomDialogFragment:

package com.example.dialogsapp;

import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;

import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;

public class CustomDialogFragment extends DialogFragment {
  @NonNull

  Public Dialog onCreateDialog(Bundle savedInstanceState) {

    String phone = getArguments().getString("phone");
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    return builder
      .setTitle("Діалогове вікно")
      .setIcon(android.R.drawable.ic_dialog_alert)
      .setMessage("Ви хочете видалити " + phone + "?")
      .setPositiveButton("OK", null)
      .setNegativeButton("Скасування", null)
      .create();
  }

}

За допомогою методу getArguments () отримуємо переданий в MainActivity об'єкт Bundle. І так як був переданий рядок, то для його вилучення

застосовується метод getString (). І при натисканні на елемент списку буде передаватися в діалогове вікно:

 

 

Взаємодія з Activity

Взаємодія між Activity і фрагментом проводиться, як правило, через інтерфейс. Наприклад, в минулій темі MainActivity виводила список об'єктів, і тепер

визначимо видалення з цього списку через діалогове вікно.

Для цього додамо в проект інтерфейс Datable:

package com.example.dialogsapp;

public interface Datable {
  void remove(String name);

}

Єдиний метод інтерфейсу remove отримує об'єкт, що буде видалено у вигляді параметра name. Тепер реалізуємо цей інтерфейс в коді MainActivity:

package com.example.dialogsapp;

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 java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements Datable {

  private ArrayList < String > phones;
  private ArrayAdapter < String > adapter;
  @Override

  public void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstance);
    setContentView(R.layout.activity_main);

    ListView phonesList = (ListView) findViewById(R.id.phonesList);
    phones = новий ArrayList < > ();

    phones.add("Google Pixel");
    phones.add("Huawei P9");
    phones.add("LG G5");
    phones.add("Samsung Galaxy S8");

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

    phonesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
      @Override

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

      }

    });

  }

  String selectedPhone = adapter.getItem(position);
  CustomDialogFragment dialog = новий CustomDialogFragment();
  Bundle args = New Bundle();

  args.putString("phone", selectedPhone);
  dialog.setArguments(args);
  dialog.show(getSupportFragmentManager(), "custom");

  @Override

  public void remove(String name) {
    adapter.remove(name);

  }

}

Метод remove просто видаляє з адаптера переданий елемент.

Файл activity_main.xml як і раніше визначає лише елемент ListView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

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

</LinearLayout>

І в кінці визначимо фрагмент CustomDialogFragment:

package com.example.dialogsapp;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;

import android.content.DialogInterface;
import android.os.Bundle;

import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;

public class CustomDialogFragment extends DialogFragment {
  private Datable datable;

  @Override

  public void onAttach(Context context) {
    super.onAttach(context);

    datable = (Datable) context;

  }

  @NonNull

  Public Dialog onCreateDialog(Bundle savedInstanceState) {

    final String phone = getArguments().getString("phone");
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    return builder

      .setTitle("Діалогове вікно")

      .setIcon(android.R.drawable.ic_dialog_alert)

      .setMessage("Ви хочете видалити " + phone + "?")

      .setPositiveButton("OK", new DialogInterface.OnClickListener() {
        @Override

        public void onClick(DialogInterface dialog, int which) {
          datable.remove(phone);

        }

      })

      .setNegativeButton("Скасування", null)

      .create();

  }

}

Метод onAttach () викликається на початку життєвого циклу фрагмента, і саме тут ми можемо отримати контекст фрагмента, в якості якого виступає клас

MainActivity. Так як MainActivity реалізує інтерфейс Datable, то ми можемо привести контекст до даного інтерфейсу.

Потім в обробнику кнопки ОК викликається метод remove об'єкта Datable, який видаляє переданий у фрагмент об'єкт phone.

 

Робота з базами даних SQLite

Підключення до бази даних SQLite

В Android є вбудована підтримка однієї з поширених систем управління базами даних - SQLite. Для цього в пакеті android.database.sqlite визначено набір

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

Щоб використовувати SQLite в Android, треба створити базу даних за допомогою виразу на мові SQL. Після цього база даних буде зберігатися в каталозі

додатку за наступним шляхом:

DATA / data / [Назвае_додатку] / databases / [Назва_файла_бази_данних]

ОС Android за замовчуванням вже містить ряд вбудованих баз SQLite, які використовуються стандартними програмами - для списку контактів, для

зберігання фотографій з камери, музичних альбомів і т.д.

Основну функціональність по роботі з базами даних надає пакет android.database.

Функціональність безпосередньо для роботи з SQLite знаходиться в пакеті android.database.sqlite.

База даних в SQLite представлена класом android.database.sqlite.SQLiteDatabase. Він дозволяє виконувати запити до бд, виконувати з нею різні

маніпуляції.

Клас android.database.sqlite.SQLiteCursor надає запит і дозволяє повертати набір рядків, які відповідають цьому запиту.

Клас android.database.sqlite.SQLiteQueryBuilder дозволяє створювати SQL-запити.

Самі sql-вирази представлені класом android.database.sqlite.SQLiteStatement, які дозволяють за допомогою плейсхолдерів вставляти в вирази

динамічні дані.

Клас android.database.sqlite.SQLiteOpenHelper дозволяє створити базу даних з усіма таблицями, якщо їх ще не існує.

У SQLite застосовується наступна система типів даних:

Дані, що зберігаються повинні відповідати типам даних в java.

 

Створення та відкриття бази даних

Для створення або відкриття нової бази даних з коду Activity в Android ми можемо викликати метод openOrCreateDatabase (). Цей метод може приймати

три параметра:

Наприклад, створення бази даних app.db:

SQLiteDatabase db = getBaseContext().OpenOrCreateDatabase( "app.db", MODE_PRIVATE, null);

Для виконання запиту до бази даних можна використовувати метод execSQL класу SQLiteDatabase. У цей метод передається SQL-вираз. Наприклад,

створення в базі даних таблиці users:

SQLiteDatabase db = getBaseContext().openOrCreateDatabase("app.db", MODE_PRIVATE, null);

db.execSQL("CREATE TABLE IF NOT EXISTS users (name TEXT, age INTEGER)");

Якщо нам треба не просто виконати вираз, але і отримати з бд будь-які дані, то використовується метод rawQuery (). Цей метод в якості параметра

приймає SQL-вираз, а також набір значень для виразу sql. Наприклад, отримання всіх об'єктів з бази даних:

SQLiteDatabase db = getBaseContext().openOrCreateDatabase("app.db", MODE_PRIVATE, null);

db.execSQL("CREATE TABLE IF NOT EXISTS users (name TEXT, age INTEGER)");

Cursor query = db.rawQuery("SELECT * FROM users;", null);
if (query.moveToFirst()) {

  String name = query.getString(0);
  int age = query.getInt(1);

}

Метод db.rawQuery () повертає об'єкт Cursor, за допомогою якого ми можемо витягти отримані дані.

Можлива ситуація, коли в базі даних не буде об'єктів, і для цього методом query.moveToFirst () намагаємося переміститися до першого об'єкту,

отриманого з бд. Якщо цей метод поверне значення false, значить запит не отримав ніяких даних з бд.

Тепер для роботи з базою даних зробимо простий додаток. Для цього створимо новий проект. У файлі 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:padding="16dp"
android:orientation="vertical">

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click"
android:onClick="onClick"/>

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />

</LinearLayout>

А в класі MainActivity визначимо взаємодію з базою даних:

package com.example.sqliteapp;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
  @Override

  protected void onCreate(Bundle savedInstanceState) {

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

  }

  public void onClick(View view) {

    SQLiteDatabase db = getBaseContext().openOrCreateDatabase("app.db", MODE_PRIVATE, null);
    db.execSQL("CREATE TABLE IF NOT EXISTS users (name TEXT, age INTEGER)");

    db.execSQL("INSERT INTO users VALUES ('Tom Smith', 23);");
    db.execSQL("INSERT INTO users VALUES ('John Dow', 31);");

    Cursor query = db.rawQuery("SELECT * FROM users;", null);
    TextView textView = (TextView) findViewById(R.id.textView);
    if (query.moveToFirst()) {

      do {

        String name = query.getString(0);
        int age = query.getInt(1);

        textView.append("Name:" + name + "Age:" + age + "\n");

      }

      while (query.moveToNext());

    }

    query.close();
    db.close();

  }

}

При натисканні на кнопку спочатку створюється в базі даних app.db нова таблиця users, а потім в неї додаються два об'єкти в базу даних за допомогою

SQL-виразу INSERT.

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

Викликом query.moveToFirst () переміщаємося в курсорі до першого об'єкту, і так як у нас може бути більше одного об'єкта, то проходимо по всьому

курсору в циклі do ... while.

Для отримання даних з курсору застосовуються методи query.getString (0) і query.getInt (1). У дужках в методи передається номер стовпчика, з якого ми

отримуємо дані. Наприклад, вище ми додали спочатку ім'я користувача у вигляді рядка, а потім вік у вигляді числа. Значить, нульовим стовпцем буде

йти рядкове значення, яке отримуємо за допомогою методу getString (), а наступним - першим стовпцем йде числове значення, для якого

застосовується метод getInt ().

Після завершення роботи з курсором і базою даних ми закриваємо всі пов'язані об'єкти:

query.close();
db.close();

Якщо ми не закриємо курсор, то можемо зіткнутися з проблемою витоку пам'яті.

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

 

 

SimpleCursorAdapter и получение даннях

У минулій темі було розглянуто, як підключатися до бази даних SQLite і виконувати запити.

Тепер підемо далі і створимо повністю інтерфейс для роботи з базою даних.

Отже, створимо новий проект.

Для спрощення роботи з базами даних SQLite в Android нерідко застосовується клас SQLiteOpenHelper. Для використання необхідно створити клас-

спадкоємець від SQLiteOpenHelper, перевизначивши як мінімум два його методи:

Тому додамо в проект, в ту ж папку, де знаходиться клас MainActivity, новий клас DatabaseHelper:

package com.example.sqliteapp;

import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.content.Context;

import android.content.ContentValues;

public class DatabaseHelper extends SQLiteOpenHelper {

  private static final String DATABASE_NAME = "userstore.db"; // Назва бд 
  private static final int SCHEMA = 1; // версія бази даних

  static final String TABLE = "users"; // Назва таблиці в бд

  // назви стовпців
  public static final String COLUMN_ID = "_id";

  public static final String COLUMN_NAME = "name";
  public static final String COLUMN_YEAR = "рок";

  public DatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, SCHEMA);

  }

  @Override

  public void onCreate(SQLiteDatabase db) {

    db.execSQL("CREATE TABLE users (" + COLUMN_ID

      +
      " INTEGER PRIMARY KEY AUTOINCREMENT," + COLUMN_NAME

      +
      "TEXT," + COLUMN_YEAR + "INTEGER);");

    // Додавання початкових даних
    db.execSQL("INSERT INTO" + TABLE + " (" + COLUMN_NAME

      +
      "," + COLUMN_YEAR + ") VALUES ('Том Сміт', 1981);");

  }

  @Override

  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS " + TABLE);

    onCreate(db);

  }

}

Якщо база даних відсутня або її версія (яка задається у змінній SCHEMA) вище поточної, то спрацьовує метод onCreate ().

Для виконання запитів до бази даних нам потрібно об'єкт SQLiteDatabase, який представляє базу даних. Метод onCreate () отримує в якості параметра

базу даних програми.

Для виконання запитів до SQLite використовується метод execSQL (). Він приймає sql-вираз CREATE TABLE, який створює таблицю. Тут також при

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

sql INSERT додається один об'єкт в таблицю.

У методі onUpgrade () відбувається оновлення схеми БД. В даному випадку для прикладу використаний примітивний похід з видаленням попередньої

бази даних за допомогою sql-виразу DROP і подальшим її створенням. Але в реальності якщо вам буде необхідно зберегти дані, цей метод може

включати більш складну логіку - додавання нових стовпців, видалення непотрібних, додавання додаткових даних і т.д.

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

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

<TextView
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18dp"/>

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

</LinearLayout>

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

І змінимо код класу MainActivity наступним чином:

package com.example.sqliteapp;
import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;
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.SimpleCursorAdapter;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
  ListView userList;

  TextView header;

  DatabaseHelper databaseHelper;
  SQLiteDatabase db;

  Cursor userCursor;
  SimpleCursorAdapter userAdapter;

  @Override

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

    header = (TextView) findViewById(R.id.header);
    userList = (ListView) findViewById(R.id.list);

    databaseHelper = new DatabaseHelper(getApplicationContext());

  }

  @Override

  public void onResume() {
    super.onResume();

    // відкриваємо підключення
    db = databaseHelper.getReadableDatabase();

    //отримуємо дані з бд як курсора
    userCursor = db.rawQuery("select * from " + DatabaseHelper.TABLE, null);

    // Визначаємо, які стовпці з курсору будуть виводитися в ListView
    String[] headers = new String[] {
      DatabaseHelper.COLUMN_NAME, DatabaseHelper.COLUMN_YEAR
    };

    // Створюємо адаптер, передаємо в нього курсор

    userAdapter = новий SimpleCursorAdapter(this, android.R.layout.two_line_list_item, userCursor, headers, new int[] {
      android.R.id.text1, android.R.id.text2
    }, 0);

    header.setText("Знайдено елементів: " + String.valueOf(userCursor.getCount()));
    userList.setAdapter(userAdapter);

  }

  @Override

  public void onDestroy() {
    super.onDestroy();

    // Закриваємо підключення та курсор 
    db.close();

    userCursor.close();

  }

}

У методі onCreate () відбувається створення об'єкта SQLiteOpenHelper. Сама ініціалізація об'єктів для роботи з базою даних відбувається в методі

onResume (), який спрацьовує після методу onCreate ().

Щоб отримати об'єкт бази даних, треба використовувати метод getReadableDatabase () (отримання бази даних для читання) або getWritableDatabase ().

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

db = sqlHelper.getReadableDatabase ();

Отримання даних і Cursor

Android надає різні способи для здійснення запитів до об'єкта SQLiteDatabase. У більшості випадків ми можемо застосовувати метод rawQuery (), який

приймає два параметри: SQL-вираз SELECT і додатковий параметр, що задає параметри запиту.

Після виконання запиту rawQuery () повертає об'єкт Cursor, який зберігає результат виконання SQL-запиту:

userCursor = db.rawQuery("select * from "+ DatabaseHelper.TABLE, null);

Клас Cursor пропонує ряд методів для управління вибіркою, зокрема:

 

CursorAdapter

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

спискових елементах на зразок ListView. Як правило, при роботі з курсором використовується підклас CursorAdapter - SimpleCursorAdapter. Хоча можна

використовувати і інші адаптери, типу ArrayAdapter.

userAdapter = new SimpleCursorAdapter(this, android.R.layout.two_line_list_item, 
                              userCursor, headers, new int[]{android.R.id.text1, android.R.id.text2}, 0);

userList.setAdapter(userAdapter);

Конструктор класу SimpleCursorAdapter приймає шість параметрів:

При використанні CursorAdapter і його підкласів слід враховувати, що вибірка курсора повинна включати цілочисельний стовпець з назвою _id, який

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

onListItemClick (), завдяки чому ми можемо по id ідентифікувати натиснутий елемент.

В даному випадку у нас перший стовпець як раз називається "_id".

Після завершення роботи курсор повинен бути закритий методом close ()

І також треба враховувати, що якщо ми використовуємо курсор в SimpleCursorAdapter, то ми не можемо використовувати метод close (), поки не

завершимо використання SimpleCursorAdapter. Тому метод cursor більш переважно викликати в методі onDestroy () фрагмента або activity. І якщо ми

запустимо додаток, то побачимо список з одного доданого елемента:

 

Додавання, видалення та оновлення даних в SQLite

Продовжимо роботу з проектом з минулої теми, де ми отримуємо дані. Тепер додамо в нього стандартну CRUD-логіку (створення, оновлення,

видалення).

Щоб не нагромаджувати форму з головною activity, всі інші дії по роботі з даними будуть відбуватися на іншому екрані. Додамо в проект новий клас

activity, який назвемо UserActivity:

У файлі activity_user.xml визначимо універсальну форму для додавання / оновлення / видалення даних:

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

<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Введіть ім'я"/>

<EditText
android:id="@+id/year"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Введіть рік народження"/>

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

<Button
android:id="@+id/saveButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Зберегти"
android:onClick="save"/>

<Button
android:id="@+id/deleteButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Видалити"
android:onClick="delete"/>

</LinearLayout>

</LinearLayout>

І також змінимо код UserActivity:

package com.example.sqliteapp;

import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class UserActivity extends AppCompatActivity {
  EditText nameBox;

  EditText yearBox;

  Button del Button;

  Button saveButton;

  DatabaseHelper sqlHelper;
  SQLiteDatabase db;
  Cursor userCursor;

  long userId = 0;
  @Override

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

    nameBox = (EditText) findViewById(R.id.name);
    yearBox = (EditText) findViewById(R.id.year);

    delButton = (Button) findViewById(R.id.deleteButton);
    saveButton = (Button) findViewById(R.id.saveButton);

    sqlHelper = New DatabaseHelper(this);
    db = sqlHelper.getWritableDatabase();

    Bundle extras = getIntent().getExtras();
    if (extras! = null) {

      userId = extras.getLong("id");

    }

    // якщо 0, то додавання 
    if (userId > 0) {

      // отримуємо елемент з id з бд
      userCursor = db.rawQuery("select * from " + DatabaseHelper.TABLE + " where " + DatabaseHelper.COLUMN_ID + "=?", new String[] {
        String.valueOf(userId)
      });

      userCursor.moveToFirst();
      nameBox.setText(userCursor.getString(1));
      yearBox.setText(String.valueOf(userCursor.getInt(2)));
      userCursor.close();

    } else {

      // приховуємо кнопку видалення 
      delButton.setVisibility(View.GONE);

    }

  }

  public void save(View view) {

    ContentValues cv = New ContentValues();
    cv.put(DatabaseHelper.COLUMN_NAME, nameBox.getText().toString());

    cv.put(DatabaseHelper.COLUMN_YEAR, Integer.parseInt(yearBox.getText().toString()));

    if (userId > 0) {

      db.update(DatabaseHelper.TABLE, cv, DatabaseHelper.COLUMN_ID + "=" + String.valueOf(userId), null);

    } else {

      db.insert(DatabaseHelper.TABLE, null, cv);

    }

    goHome();

  }

  public void delete(View view) {

    db.delete(DatabaseHelper.TABLE, "_id = ?", new String[] {
      String.valueOf(userId)
    });
    goHome();

  }

  private void goHome() {

    // Закриваємо підключення 
    db.close();

    // Перехід до головної Актівіті
    Intent intent = new Intent(this, MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    startActivity(intent);

  }

}

При оновленні або видаленні об'єкта зі списку з головною activity в UserActivity передаватиметься id об'єкта:

long userId = 0;

//.................

Bundle extras = getIntent().getExtras();
if (extras != null) {

  userId = extras.getLong("id");

}

Якщо з MainActivity не було передано id, то встановлюємо його значення 0, отже, у нас буде додавання, а не редагування / знищення

Якщо id визначено, то отримуємо по ньому з бази даних об'єкт для редагування / видалення:

if (id < 0) {

  userCursor = db.rawQuery("select * from " + DatabaseHelper.TABLE + " where " + DatabaseHelper.COLUMN_ID + "=?", new String[] {
    String.valueOf(id)
  });

  userCursor.moveToFirst();
  nameBox.setText(userCursor.getString(1));
  yearBox.setText(String.valueOf(userCursor.getInt(2)));
  userCursor.close();

}

Інакше просто приховуємо кнопку видалення.

Для виконання операцій по вставці, оновленню і видаленню даних SQLiteDatabase має методи insert (), update () і delete (). Ці методи викликаються в

обробниках кнопок:

db.delete(DatabaseHelper.TABLE, "_id = ?", new String[]{String.valueOf(id)});

У метод delete () передається назва таблиці, а також стовпець, по якому відбувається видалення, і його значення. В якості критерію можна вибрати

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

 

ContentValues

Для додавання або оновлення нам треба створити об'єкт ContentValues. Даний об'єкт являє словник, який містить набір пар "ключ-значення". Щоб

додати до цього словника нового об'єкта застосовується метод put. Перший параметр методу - це ключ, а другий - значення, наприклад:

ContentValues cv = new ContentValues();
cv.put("NAME", "Tom");
cv.put("YEAR", 30);

Як значення в метод put можна передавати рядки, цілі числа, числа з плаваючою точкою. В даному ж випадку додаються введені в текстове поля значення:

ContentValues cv = new ContentValues ();
cv.put (DatabaseHelper.COLUMN_NAME, nameBox.getText (). toString ());
cv.put (DatabaseHelper.COLUMN_YEAR, Integer.parseInt (yearBox.getText (). toString ()));

При оновленні в метод update () передається назва таблиці, об'єкт ContentValues і критерій, за яким відбувається оновлення (в даному випадку стовпець id):

db.update (DatabaseHelper.TABLE, cv, DatabaseHelper.COLUMN_ID + "=" + String.valueOf (id), null);

Метод insert () приймає назву таблиці, об'єкт ContentValues з значеннями, що додаються.

Другий параметр є необов'язковим: він передає стовпець, в який треба додати значення NULL:

db.insert (DatabaseHelper.TABLE, null, cv);

Замість цих методів, як в минулій темі, можна використовувати метод execSQL () з точним зазначенням виконуваного sql-виразу. У той же час методи

delete / insert / update мають перевагу - вони повертають id зміненого запису, за яким ми можемо дізнатися про успішність операції, або -1 у випадку

невдалої операції:

long result = db.insert(DatabaseHelper.TABLE, null, cv);
if (result > 0) {

  // дії

}

Після кожної операції виконується метод goHome (), який повертає на головну activity.

Після цього нам треба виправити код MainActivity, щоб вона ініціювала виконання коду в UserActivity. Для цього змінимо код activity_main.xml:

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

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Додати"
android:onClick="add"
android:textSize="18dp"/>

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

</LinearLayout>

В даному випадку була додана кнопка для виклику UserActivity. І також змінимо код класу MainActivity:

package com.example.sqliteapp;

import android.content.Intent;
import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;
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.SimpleCursorAdapter;

public class MainActivity extends AppCompatActivity {
  ListView userList;

  DatabaseHelper databaseHelper;

  SQLiteDatabase db;
  Cursor userCursor;

  SimpleCursorAdapter userAdapter;
  @Override

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

    userList = (ListView) findViewById(R.id.list);
    userList.setOnItemClickListener(new AdapterView.OnItemClickListener() {

      @Override

      public void onItemClick(AdapterView < ? > parent, View view, int position, long id) {
        Intent intent = new Intent(getApplicationContext(), UserActivity.class);
        intent.putExtra("id", id);

        startActivity(intent);

      }

    });

    databaseHelper = new DatabaseHelper(getApplicationContext());

  }

  @Override

  public void onResume() {
    super.onResume();

    // відкриваємо підключення
    db = databaseHelper.getReadableDatabase();

    //отримуємо дані з бд як курсора
    userCursor = db.rawQuery("select * from " + DatabaseHelper.TABLE, null);

    // Визначаємо, які стовпці з курсору будуть виводитися в ListView
    String[] headers = new String[] {
      DatabaseHelper.COLUMN_NAME, DatabaseHelper.COLUMN_YEAR
    };

    // Створюємо адаптер, передаємо в нього курсор
    userAdapter = новий SimpleCursorAdapter(this, android.R.layout.two_line_list_item, userCursor, headers, new int[] {
      android.R.id.text1, android.R.id.text2
    }, 0);

    userList.setAdapter(userAdapter);

  }

  // натисканням на кнопку запускаємо UserActivity для додавання даних 
  public void add(View view) {

    Intent intent = new Intent(this, UserActivity.class);
    startActivity(intent);

  }

  @Override

  public void onDestroy() {
    super.onDestroy();

    // Закриваємо підключення та курсор db.close();
    userCursor.close();

  }

}

При натисканні на кнопку запускається UserActivity, при цьому не передається ніякого id, тобто в UserActivity id буде дорівнюєвати нулю, значить буде

йти додавання даних:

public void add(View view) {

  Intent intent = new Intent(this, UserActivity.class);
  startActivity(intent);

}

Іншу ситуацію представляє обробник натискання на елемент списку - при натисканні також буде запускатися UserActivity, але тепер буде передаватися

id обраного запису:

public void onItemClick(AdapterView < ? > parent, View view, int position, long id) {
  Intent intent = new Intent(getApplicationContext(), UserActivity.class);
  intent.putExtra("id", id);

  startActivity(intent);

}

Запустимо програму і натиснемо на кнопку, яка має перенаправляти на UserActivity:

При натисканні в MainActivity на елемент списку цей елемент потрапить на UserActivity, де його можна буде видалити або відредагувати:

 

Використання існуючої БД SQLite

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

програми вже буде містити всю необхідну інформацію.

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

Sqlitebrowser. Він безкоштовний і доступний для різних операційних систем за адресою http://sqlitebrowser.org/. Хоча можна використовувати і інші

способи для створення початкової БД.

Sqlitebrowser представляє графічний інтерфейс для створення бази даних і визначення в ній всіх необхідних таблиць:

Як видно на скріншоті, я визначаю таблицю users з трьома полями: _id, name, age. Загальна команда на створення таблиці буде наступною:

CREATE TABLE `users`(
  `_id`
  INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
  `name`
  TEXT NOT NULL,
  `year`
  INTEGER NOT NULL
);

Там же в програмі додамо кілька елементів в створену таблицю:

 

Після створення таблиці додамо в проект в Android Studio папку assets, а в папку assets - щойно створену базу даних. Для цього перейдемо до повного

визначення проекту, натиснемо на папку main правою кнопкою миші і в меню виберемо New -> Directory:

Назвемо додається папку assets і потім скопіюємо в неї нашу базу даних:

 

У нашому випадку база даних називається "cityinfo.db". Змінимо код DatabaseHelper наступним чином:

package com.example.sqliteapp;

import android.database.SQLException;

import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.content.Context;

import android.util.Log;

import java.io.File;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

class DatabaseHelper extends SQLiteOpenHelper {

  private static String DB_PATH; // Повний шлях до бази даних 
  private static String DB_NAME = "cityinfo.db";

  private static final int SCHEMA = 1; // версія бази даних

  static final String TABLE = "users"; // Назва таблиці в бд

  // назви стовпців
  static final String COLUMN_ID = "_id";
  static final String COLUMN_NAME = "name";
  static final String COLUMN_YEAR = "рік";
  private Context myContext;

  DatabaseHelper(Context context) {
    super(context, DB_NAME, null, SCHEMA);
    this.myContext = context;

    DB_PATH = context.getFilesDir().getPath() + DB_NAME;

  }

  @Override

  public void onCreate(SQLiteDatabase db) {

  }

  @Override

  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

  }

  void create_db() {

    InputStream myInput = null;
    OutputStream myOutput = null;
    try {

      File file = new File(DB_PATH);
      if (!file.exists()) {

        this.getReadableDatabase();

        //отримуємо локальну бд як потік
        myInput = myContext.getAssets().open(DB_NAME);

        // Шлях до нової бд
        String outFileName = DB_PATH;

        // Відкриваємо порожню бд
        myOutput = новий FileOutputStream(outFileName);

        // Побайтово копіюємо дані 
        byte[] buffer = new byte[1024];
        int length;

        while ((length = myInput.read(buffer)) > 0) {
          myOutput.write(buffer, 0, length);

        }

        myOutput.flush();
        myOutput.close();
        myInput.close();

      }

    } catch (IOException ex) {
      Log.d("DatabaseHelper", ex.getMessage());

    }

  }

  public SQLiteDatabase open() throws SQLException {

    return SQLiteDatabase.openDatabase(DB_PATH, null, SQLiteDatabase.OPEN_READWRITE);

  }

}

За замовчуванням база даних буде розміщуватися в зовнішньому сховищі, що виділяється для програми у папці / data / data / [назва_пакета] / databases

/, і щоб отримати повний шлях до бази даних в конструкторі використовується вираз:

DB_PATH = context.getFilesDir ().GetPath () + DB_NAME;

Метод onCreate () нам не потрібен, так як нам не потрібне створення вбудованої бази даних. Зате тут визначено додатковий метод create_db (), мета

якого копіювання бази даних з папки assets в те місце, яке зазначено в змінній DB_PATH.

Крім цього тут також визначено метод відкриття бази даних open () за допомогою методу SQLiteDatabase.openDatabase ()

Новий спосіб організації підключення змінить використання DatabaseHelper в activity. Так, оновимо клас MainActivity:

package com.example.sqliteapp;

import android.content.Intent;
import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;
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.SimpleCursorAdapter;

public class MainActivity extends AppCompatActivity {
  ListView userList;

  DatabaseHelper databaseHelper;
  SQLiteDatabase db;

  Cursor userCursor;
  SimpleCursorAdapter userAdapter;

  @Override

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

    userList = (ListView) findViewById(R.id.list);
    userList.setOnItemClickListener(new AdapterView.OnItemClickListener() {

      @Override

      public void onItemClick(AdapterView < ? > parent, View view, int position, long id) {
        Intent intent = new Intent(getApplicationContext(), UserActivity.class);
        intent.putExtra("id", id);

        startActivity(intent);

      }

    });

    databaseHelper = new DatabaseHelper(getApplicationContext());

    // створюємо базу даних 
    databaseHelper.create_db();

  }

  @Override

  public void onResume() {
    super.onResume();

    // відкриваємо підключення
    db = databaseHelper.open();

    //отримуємо дані з бд як курсора
    userCursor = db.rawQuery("select * from " + DatabaseHelper.TABLE, null);

    // Визначаємо, які стовпці з курсору будуть виводитися в ListView
    String[] headers = new String[] {
      DatabaseHelper.COLUMN_NAME, DatabaseHelper.COLUMN_YEAR
    };

    // Створюємо адаптер, передаємо в нього курсор
    userAdapter = новий SimpleCursorAdapter(this, android.R.layout.two_line_list_item, userCursor, headers, new int[] {
      android.R.id.text1, android.R.id.text2
    }, 0);

    userList.setAdapter(userAdapter);

  }

  // натисканням на кнопку запускаємо UserActivity для додавання даних 
  public void add(View view) {

    Intent intent = new Intent(this, UserActivity.class);
    startActivity(intent);

  }

  @Override

  public void onDestroy() {
    super.onDestroy();

    // Закриваємо підключення та курсор db.close();
    userCursor.close();

  }

}

І також змінимо клас UserActivity:

package com.example.sqliteapp;

import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class UserActivity extends AppCompatActivity {
  EditText nameBox;

  EditText yearBox;

  Button del Button;

  Button saveButton;

  DatabaseHelper sqlHelper;
  SQLiteDatabase db;
  Cursor userCursor;

  long userId = 0;
  @Override

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

    nameBox = (EditText) findViewById(R.id.name);
    yearBox = (EditText) findViewById(R.id.year);

    delButton = (Button) findViewById(R.id.deleteButton);
    saveButton = (Button) findViewById(R.id.saveButton);

    sqlHelper = New DatabaseHelper(this);
    db = sqlHelper.open();

    Bundle extras = getIntent().getExtras();
    if (extras! = null) {

      userId = extras.getLong("id");

    }

    // якщо 0, то додавання 
    if (userId > 0) {

      // отримуємо елемент з id з бд
      userCursor = db.rawQuery("select * from " + DatabaseHelper.TABLE + " where " + DatabaseHelper.COLUMN_ID + "=?", new String[] {
        String.valueOf(userId)
      });

      userCursor.moveToFirst();
      nameBox.setText(userCursor.getString(1));
      yearBox.setText(String.valueOf(userCursor.getInt(2)));
      userCursor.close();

    } else {

      // приховуємо кнопку видалення 
      delButton.setVisibility(View.GONE);

    }

  }

  public void save(View view) {

    ContentValues cv = New ContentValues();
    cv.put(DatabaseHelper.COLUMN_NAME, nameBox.getText().toString());

    cv.put(DatabaseHelper.COLUMN_YEAR, Integer.parseInt(yearBox.getText().toString()));

    if (userId > 0) {

      db.update(DatabaseHelper.TABLE, cv, DatabaseHelper.COLUMN_ID + "=" + String.valueOf(userId), null);

    } else {

      db.insert(DatabaseHelper.TABLE, null, cv);

    }

    goHome();

  }

  public void delete(View view) {

    db.delete(DatabaseHelper.TABLE, "_id = ?", new String[] {
      String.valueOf(userId)
    });
    goHome();

  }

  private void goHome() {

    // Закриваємо підключення 
    db.close();

    // Перехід до головної діяльності
    Intent intent = new Intent(this, MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    startActivity(intent);

  }
}

 

 

Динамічний пошук по базі даних SQLite

Розглянемо, як ми можемо створити в додатку на Android динамічний пошук по базі даних SQLite.

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

має одну таблицю users з трьома полями _id, name, age:

CREATE TABLE `users` (
`_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
`name` TEXT NOT NULL,
`year` INTEGER NOT NULL
);

 

І також додамо в проект в Android Studio папку assets, а в папку assets - щойно створену базу  даних:

Як показано вище на скріншоті, необхідно додати в проект в одну папку з MainActivity новий клас DatabaseHelper:

package com.example.livedbsearchapp;

import android.database.SQLException;

import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.content.Context;

import android.util.Log;

import java.io.File;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

class DatabaseHelper extends SQLiteOpenHelper {

  private static String DB_PATH; // Повний шлях до бази даних 
  private static String DB_NAME = "cityinfo.db";

  private static final int SCHEMA = 1; // версія бази даних

  static final String TABLE = "users"; // Назва таблиці в бд

  // назви стовпців
  static final String COLUMN_ID = "_id";

  static final String COLUMN_NAME = "name";
  static final String COLUMN_YEAR = "рік";
  private Context myContext;

  DatabaseHelper(Context context) {
    super(context, DB_NAME, null, SCHEMA);
    this.myContext = context;

    DB_PATH = context.getFilesDir().getPath() + DB_NAME;

  }

  @Override

  public void onCreate(SQLiteDatabase db) {

  }

  @Override

  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

  }

  void create_db() {

    InputStream myInput = null;
    OutputStream myOutput = null;
    try {

      File file = new File(DB_PATH);
      if (!file.exists()) {

        this.getReadableDatabase();

        //отримуємо локальну бд як потік
        myInput = myContext.getAssets().open(DB_NAME);

        // Шлях до нової бд
        String outFileName = DB_PATH;

        // Відкриваємо порожню бд
        myOutput = новий FileOutputStream(outFileName);

        // Побайтово копіюємо дані 
        byte [] buffer = new byte [1024]; int length;

        while ((length = myInput.read(buffer)) > 0) {
          myOutput.write(buffer, 0, length);

        }

        myOutput.flush();
        myOutput.close();
        myInput.close();

      }

    } catch (IOException ex) {
      Log.d("DatabaseHelper", ex.getMessage());

    }

  }

  SQLiteDatabase open() throws SQLException {

    return SQLiteDatabase.openDatabase(DB_PATH, null, SQLiteDatabase.OPEN_READWRITE);

  }

}

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<EditText
android:id="@+id/userFilter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Пошук" />

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

</LinearLayout>

Отже, у нас буде елемент ListView для відображення списку і текстове поле для фільтрації. Тепер змінимо код MainActivity:

package com.example.livedbsearchapp;

import android.database.Cursor;
import android.database.SQLException;

import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;

import android.widget.FilterQueryProvider;
import android.widget.ListView;

import android.widget.SimpleCursorAdapter;

public class MainActivity extends AppCompatActivity {
  DatabaseHelper sqlHelper;

  SQLiteDatabase db;
  Cursor userCursor;

  SimpleCursorAdapter userAdapter;
  ListView userList;

  EditText userFilter;

  @Override

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

    userList = (ListView) findViewById(R.id.userList);
    userFilter = (EditText) findViewById(R.id.userFilter);

    sqlHelper = New DatabaseHelper(getApplicationContext());

    // Створюємо базу даних 
    sqlHelper.create_db();

  }

  @Override

  public void onResume() {
    super.onResume();
    try {

      db = sqlHelper.open();

      userCursor = db.rawQuery("select * from " + DatabaseHelper.TABLE, null);
      String[] headers = new String[] {
        DatabaseHelper.COLUMN_NAME,

          DatabaseHelper.COLUMN_YEAR
      };

      userAdapter = новий SimpleCursorAdapter(this, android.R.layout.two_line_list_item, userCursor, headers, new int[] {
        android.R.id.text1, android.R.id.text2
      }, 0);

      // якщо текстовому полі є текст, виконуємо фільтрацію

      // Ця перевірка потрібна під час переходу від однієї орієнтації екрану до іншої 
      if(!userFilter.getText().toString().isEmpty())

      userAdapter.getFilter().filter(userFilter.getText().toString());

      // встановлення слухача зміни тексту 
      userFilter.addTextChangedListener(new TextWatcher() {

      public void afterTextChanged(Editable s) {}

      public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

      // при зміні тексту виконуємо фільтрацію
      public void onTextChanged(CharSequence s, int start, int before, int count) {

        userAdapter.getFilter().filter(s.toString());

      }

    });

  // встановлюємо провайдер фільтрації 
  userAdapter.setFilterQueryProvider(new FilterQueryProvider() {

  @Override

  Public Cursor runQuery(CharSequence constraint) {

    if (constraint == null || constraint.length() == 0) {

      return db.rawQuery("select * from " + DatabaseHelper.TABLE, null);

    } else {

      return db.rawQuery("select * from " + DatabaseHelper.TABLE + " where " + DatabaseHelper.COLUMN_NAME + " like ?", new String[] {
        "%" +

        constraint.toString() + "%"
      });

    }

  }

});

userList.setAdapter(userAdapter);

}

catch (SQLException ex) {}

}

@Override

public void onDestroy() {
  super.onDestroy();

  // Закриваємо підключення та курсор 
  db.close();
  userCursor.close();

}

}

Перш за все треба відзначити, що для фільтрації даних в адаптер, нам треба отримати фільтр адаптера, а у цього фільтра виконати метод filter ():

userAdapter.getFilter().filter(s.toString());

У цей метод filter () передається ключ пошуку.

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

userFilter.addTextChangedListener(new TextWatcher() {

  public void afterTextChanged(Editable s) {

  }

  public void beforeTextChanged(CharSequence s, int start, int count, int after) {

  }

  // при зміні тексту виконуємо фільтрацію
  public void onTextChanged(CharSequence s, int start, int before, int count) {

    userAdapter.getFilter().filter(s.toString());

  }

});

У слухачі TextWatcher в методі onTextChanged якраз і викликається метод filter (), в який передет введена користувачем в текстове поле послідовність

символів.

Сам виклик методу filter () мало на що впливає. Нам потрібно ще визначити провайдер фільтрації адаптера, який і буде інкапсулювати реальну логіку

фільтрації:

userAdapter.setFilterQueryProvider(new FilterQueryProvider() {

    @Override

    Public Cursor runQuery(CharSequence constraint) {

      if (constraint == null || constraint.length() == 0) {

        return db.rawQuery("select * from " + DatabaseHelper.TABLE, null);

      }

      "%"
    });

  else {

    return db.rawQuery("select * from " + DatabaseHelper.TABLE + " where " + DatabaseHelper.COLUMN_NAME + " like ?", new String[] {
        "%" + constraint.toString() +

      }

    }

  });

Сутність цього провайдера полягає у виконанні SQL-виразів до бд, а саме конструкцій "select from" і "select from where like". Дані найпростіші вирази

виконують чутливу до регістру фільтрацію. В результаті адаптер отримує відфільтровані дані.

Слід також відзначити наступний код:

if(!userFilter.getText().toString().isEmpty())
userAdapter.getFilter().filter(userFilter.getText().toString());

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

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

 

 

 

Модель, репозиторій і робота з базою даних

У минулих темах було розглянуто взаємодію з базою даних через клас SimpleCursorAdapter. Але є й інші способи роботи з даними, коли ми

абстрагуємося від структури таблиці і працюємо через модель, а вся взаємодія з базою даних здійснюється фактично через реалізацію паттерна

репозиторій.

Так, створимо новий проект і перш за все додамо в нього клас моделі, який назвемо User:

package com.example.databaseadapterapp;
public class User {

  private long id;
  private String name;
  private int year;

  User(long id, String name, int year) {

    this.id = id;
    this.name = name;
    this.year = year;

  }

  public long getId() {
    return id;

  }

  public String getName() {
    return name;

  }

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

  }

  public int getYear() {
    return year;

  }

  public void setYear(int year) {
    this.year = year;

  }

  @Override

  public String toString() {

    return this.name + ":" + this.year;

  }

}

В даному проекті ми будемо працювати фактично з тими ж даними, що і раніше з даними користувачів, у яких є унікальний ідентифікатор, ім'я та рік

народження. І модель User якраз описує ці дані.

Для взаємодії з базою даних SQLite додамо новий клас DatabaseHelper:

package com.example.databaseadapterapp;

import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.content.Context;

import android.content.ContentValues;

public class DatabaseHelper extends SQLiteOpenHelper {

  private static final String DATABASE_NAME = "userstore.db"; // Назва бд 
  private static final int SCHEMA = 1; // версія бази даних

  static final String TABLE = "users"; // Назва таблиці в бд

  // назви стовпців
  public static final String COLUMN_ID = "_id";
  public static final String COLUMN_NAME = "name";
  public static final String COLUMN_YEAR = "рік";

  public DatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, SCHEMA);

  }

  @Override

  public void onCreate(SQLiteDatabase db) {

    db.execSQL("CREATE TABLE" + TABLE + "(" + COLUMN_ID

      +
      " INTEGER PRIMARY KEY AUTOINCREMENT," + COLUMN_NAME

      +
      "TEXT," + COLUMN_YEAR + "INTEGER);");

    // Додавання початкових даних
    db.execSQL("INSERT INTO" + TABLE + " (" + COLUMN_NAME

      +
      "," + COLUMN_YEAR + ") VALUES ('Том Сміт', 1981);");

  }

  @Override

  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS " + TABLE);

    onCreate(db);

  }

}

Також для роботи з базою даних додамо в проект клас DatabaseAdapter:

package com.example.databaseadapterapp;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;

import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;

import java.util.List;

public class DatabaseAdapter {

  private DatabaseHelper dbHelper;
  private SQLiteDatabase database;

  public DatabaseAdapter(Context context) {

    dbHelper = New DatabaseHelper(context.getApplicationContext());

  }

  public DatabaseAdapter open() {

    database = dbHelper.getWritableDatabase();
    return this;

  }

  public void close() {
    dbHelper.close();

  }

  private Cursor getAllEntries() {

    String[] columns = new String[] {
      DatabaseHelper.COLUMN_ID, DatabaseHelper.COLUMN_NAME, DatabaseHelper.COLUMN_YEAR
    };

    return database.query(DatabaseHelper.TABLE, columns, null, null, null, null, null);

  }

  public List < User > getUsers() {

    ArrayList < User > users = new ArrayList < > ();
    Cursor cursor = getAllEntries();
    if (cursor.moveToFirst()) {

      do {

        int id = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_ID));

        String name = cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_NAME));
        int year = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_YEAR));
        users.add(new User(id, name, year));

      }

      while (cursor.moveToNext());

    }

    cursor.close();
    return users;

  }

  public long getCount() {

    return DatabaseUtils.queryNumEntries(database, DatabaseHelper.TABLE);

  }

  public User getUser(long id) {
    User user = null;

    String query = String.format("SELECT * FROM %s WHERE %s=?", DatabaseHelper.TABLE, DatabaseHelper.COLUMN_ID);

    Cursor cursor = database.rawQuery(query, new String[] {
      String.valueOf(id)
    });
    if (cursor.moveToFirst()) {

      String name = cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_NAME));
      int year = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_YEAR));

      user = new User(id, name, year);

    }

    cursor.close();
    return user;

  }

  public long insert(User user) {

    ContentValues cv = New ContentValues();
    cv.put(DatabaseHelper.COLUMN_NAME, user.getName());
    cv.put(DatabaseHelper.COLUMN_YEAR, user.getYear());

    return database.insert(DatabaseHelper.TABLE, null, cv);

  }

  public long delete(long userId) {

    String whereClause = "_id = ?";

    String[] whereArgs = new String[] {
      String.valueOf(userId)
    };

    return database.delete(DatabaseHelper.TABLE, whereClause, whereArgs);

  }

  public long update(User user) {

    String whereClause = DatabaseHelper.COLUMN_ID + "=" + String.valueOf(user.getId());
    ContentValues cv = New ContentValues();

    cv.put(DatabaseHelper.COLUMN_NAME, user.getName());
    cv.put(DatabaseHelper.COLUMN_YEAR, user.getYear());

    return database.update(DatabaseHelper.TABLE, cv, whereClause, null);

  }

}

Фактично даний клас виконує роль сховища даних. Щоб взажмодіяти з БД він визначає методи open () і close (), які відповідно відкривають і закривають

підключення до бази даних.

Безпосередньо для роботи з даними в класі визначено методи insert () (додавання), delete () (видалення), update () (оновлення), getUsers () (отримання

всіх користувачів з таблиці) і getUser () (отримання одного користувача по id ).

Так, додамо в проект новий клас Activity - UserActivity:

package com.example.databaseadapterapp;

import android.content.Intent;

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

import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class UserActivity extends AppCompatActivity {
  private EditText nameBox;

  private EditText yearBox;

  private Button del Button;
  private Button saveButton;

  private DatabaseAdapter adapter;
  private long userId = 0;

  @Override

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

    nameBox = (EditText) findViewById(R.id.name);
    yearBox = (EditText) findViewById(R.id.year);

    delButton = (Button) findViewById(R.id.deleteButton);
    saveButton = (Button) findViewById(R.id.saveButton);
    adapter = new DatabaseAdapter(this);

    Bundle extras = getIntent().getExtras();
    if (extras! = null) {

      userId = extras.getLong("id");

    }

    // якщо 0, то додавання 
    if (userId> 0) {

    // отримуємо елемент з id з бд adapter.open();
    User user = adapter.getUser(userId);
    nameBox.setText(user.getName());
    yearBox.setText(String.valueOf(user.getYear()));
    adapter.close();

  } else {

    // приховуємо кнопку видалення 
    delButton.setVisibility(View.GONE);

  }

}

public void save(View view) {

  String name = nameBox.getText().toString();

  int year = Integer.parseInt(yearBox.getText().toString());
  User user = new User(userId, name, year);

  adapter.open();
  if (userId > 0) {

    adapter.update(user);

  } else {

    adapter.insert(user);

  }

  adapter.close();
  goHome();

}

public void delete(View view) {

  adapter.open();
  adapter.delete(userId);
  adapter.close();
  goHome();

}

private void goHome() {

  // Перехід до головної актівіті
  Intent intent = new Intent(this, MainActivity.class);
  intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
  startActivity(intent);

}

}

Ця activity використовується для додавання / редагування / видалення одного об'єкта User. Якщо в UserActivity передається параметр id, то значить ми

знаходимося в режимі редагування користувача, тому звертаємося до методу getUser () класу DatabaseAdapter для отримання потрібного

користувача.

Для додавання / зміни / видалення користувача після натискання на кнопку викликається відповідний метод класу DatabaseAdapter.

У файлі activity_user.xml в папці res / layout визначимо для UserActivity найпростіший інтерфейс:

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

<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Введіть ім'я"/>

<EditText
android:id="@+id/year"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Введіть рік народження"/>

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

<Button
android:id="@+id/saveButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Зберегти"
android:onClick="save"/>

<Button
android:id="@+id/deleteButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Видалити"
android:onClick="delete"/>

</LinearLayout>

</LinearLayout>

У файлі activity_main.xml в папці res / layout визначимо візуальний інтерфейс для MainActivity:

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

<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Введіть ім'я"/>

<EditText
android:id="@+id/year"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Введіть рік народження"/>

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

<Button
android:id="@+id/saveButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Зберегти"
android:onClick="save"/>

<Button
android:id="@+id/deleteButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Видалити"
android:onClick="delete"/>

</LinearLayout>

</LinearLayout>

Тут є елемент ListView для виведення об'єктів з таблиці і кнопка для переходу до UserActivity

для додавання користувача.

І змінимо код MainActivity:

package com.example.databaseadapterapp;

import android.content.Intent;

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 java.util.List;

public class MainActivity extends AppCompatActivity {
  private ListView userList;

  ArrayAdapter < User > arrayAdapter;
  @Override

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

    userList = (ListView) findViewById(R.id.list);
    userList.setOnItemClickListener(new AdapterView.OnItemClickListener() {

      @Override

      public void onItemClick(AdapterView < ? > parent, View view, int position, long id) {
        User user = arrayAdapter.getItem(position);

        if (user != null) {

          Intent intent = new Intent(getApplicationContext(), UserActivity.class);
          intent.putExtra("id", user.getId());

          intent.putExtra("click", 25);
          startActivity(intent);

        }

      }

    });

  }

  @Override

  public void onResume() {
    super.onResume();

    DatabaseAdapter adapter = new DatabaseAdapter(this);
    adapter.open();

    List < User > users = adapter.getUsers();

    arrayAdapter = новий ArrayAdapter < > (this, android.R.layout.simple_list_item_1, users);
    userList.setAdapter(arrayAdapter);

    adapter.close();

  }

  // натисканням на кнопку запускаємо UserActivity для додавання даних 
  public void add(View view){

  Intent intent = new Intent(this, UserActivity.class);
  startActivity(intent);

}

}

У перевизначенні метода onResume () через об'єкт DatabaseAdapter отримуємо всіх користувачів з бази даних і через ArrayAdapter виводимо їх в

ListView.

При натисканні на елемент ListView запускаємо UserActivity, передаючи їй id виділеного користувача.

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

При запуску MainActivity відобразить список користувачів з бази даних, а при переході до UserActivity ми зможемо підредагувати або додати

користувачів:

 

Гортання сторінок і ViewPager

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

У додатку Android для створення подібного ефекту можна використовувати елемент ViewPager. Для створення ефекту сторінок він використовує

фрагменти.

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

fragment_page.xml і визначимо в ньому наступний код:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/displayText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:layout_gravity="center" />

</FrameLayout>

Фрагмент буде відображати текстове поле з номером сторінки.

Тепер додамо в проект сам клас фрагмента. Назвемо його PageFragment:

package com.example.viewpagerapp;

import android.os.Bundle;

import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;

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

public class PageFragment extends Fragment {
  private int pageNumber;

  public static PageFragment newInstance(int page) {
    PageFragment fragment = new PageFragment();
    Bundle args = новий Bundle();

    args.putInt("num", page);
    fragment.setArguments(args);
    return fragment;

  }

  public PageFragment() {

  }

  @Override

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    pageNumber = getArguments() != null ? getArguments().getInt("num") : 1;

  }

  @Override

  Public View onCreateView(LayoutInflater inflater, ViewGroup container,

    Bundle savedInstanceState) {

    View result = inflater.inflate(R.layout.fragment_page, container, false);
    TextView pageHeader = (TextView) result.findViewById(R.id.displayText);
    String header = String.format("Фрагмент %d", pageNumber + 1);
    pageHeader.setText(header);

    return result;

  }

}

Змінна pageNumber вказує на номер поточної сторінки. Номер сторінки буде передаватися ззовні через фабричний метод newInstance (). Передача

номера відбувається шляхом додавання значення в аргумент "num"

Потім при створенні фрагмента в методі onCreate () цей номер буде вилучатись з аргументу "num" (якщо аргументи визначені):

pageNumber = getArguments ()! = null ? getArguments (). getInt ( "num") : 1;

У методі onCreateView () отриманий номер сторінки буде відображатися в текстовому полі.

Сам по собі фрагмент ще не створює функціональність посторінкової навігації. Для цього нам потрібен один з класів PagerAdapter. Android SDK містить

дві вбудовані реалізації PagerAdapter: класи FragmentPagerAdapter і FragmentStatePagerAdapter.

FragmentPagerAdapter зберігає всю кількість використовуваних фрагментів в пам'яті і тому підходить, якщо в додатку невелика кількість сторінок. А клас

FragmentStatePagerAdapter зберігає в пам'яті тільки поточну, попередню і наступну сторінку (при їх наявності). За рахунок цього він економніше витрачає

пам'ять, але в той же час працює трохи повільніше. В даному випадку скористаємося класом FragmentPagerAdapter.

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

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

package com.example.viewpagerapp;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;

import android.support.v4.app.FragmentPagerAdapter;

public class MyAdapter extends FragmentPagerAdapter {
  public MyAdapter(FragmentManager mgr) {

    super(mgr);

  }

  @Override

  public int getCount() {
    return (10);

  }

  @Override

  public Fragment getItem(int position) {
    return (PageFragment.newInstance(position));

  }

}

Клас FragmentPagerAdapter визначає два методи:

На завершення встановимо в файлі activity_main.xml елемент ViewPager:

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">

</android.support.v4.view.ViewPager>

І також змінимо код MainActivity:

package com.example.viewpagerapp;

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

import android.view.Menu;
import android.view.MenuItem;

import android.support.v4.view.ViewPager;

public class MainActivity extends ActionBarActivity {
  @Override

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

    ViewPager pager = (ViewPager) findViewById(R.id.pager);
    pager.setAdapter(new MyAdapter(getSupportFragmentManager()));

  }

}

Щоб перегортання запрацювало, встановимо для ViewPager адаптер MyAdapter.

І запустивши проект, ми зможемо за допомогою перегортання переміщатися по сторінках:

 

Заголовки сторінок, PagerTitleStrip і PagerTabStrip

Додамо до сторінок заголовки, за допомогою яких ми можемо додатково переміщатися по сторінках.

Для додавання заголовків ми можемо використовувати елементи PagerTitleStrip і PagerTabStrip. Головна різниця між ними полягає в тому, що заголовки

в PagerTabStrip фактично представляють собою інтерактивні вкладки, натиснувши на які, ми можемо перейти до певної сторінки. А на елементі

PagerTitleStrip всі заголовки просто є статичним текстом.

Використовуємо PagerTabStrip. Для цього візьмемо проект з минулої теми. Спочатку змінимо файл activity_main.xml:

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.view.PagerTabStrip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"/>

</android.support.v4.view.ViewPager>

Змінимо код фрагмента PageFragment, щоб він міг встановлювати заголовок вкладок:

package com.example.viewpagerapp;

import android.content.Context;
import android.os.Bundle;

import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;

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

public class PageFragment extends Fragment {
  private int pageNumber;

  public static PageFragment newInstance(int page) {
    PageFragment fragment = new PageFragment();
    Bundle args = новий Bundle();

    args.putInt("num", page);
    fragment.setArguments(args);

    return fragment;

  }

  public PageFragment() {

  }

  @Override

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    pageNumber = getArguments() != null ? getArguments().getInt("num") : 1;

  }

  static String getTitle(Context context, int position) {
    return "Сторінка №" + String.valueOf(position + 1);

  }

  @Override

  Public View onCreateView(LayoutInflater inflater, ViewGroup container,

    Bundle savedInstanceState) {

    View result = inflater.inflate(R.layout.fragment_page, container, false);
    TextView pageHeader = (TextView) result.findViewById(R.id.displayText);
    pageHeader.setText("Фрагмент" + String.valueOf(pageNumber + 1));

    return result;

  }

}

Тут доданий метод getTitle (), завдання якого - установка заголовка сторінки. Тепер змінимо код адаптера MyAdapter:

package com.example.viewpagerapp;

import android.content.Context;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;

import android.support.v4.app.FragmentPagerAdapter;

public class MyAdapter extends FragmentPagerAdapter {
  private Context context = null;

  public MyAdapter(Context context, FragmentManager mgr) {
    super(mgr);

    this.context = context;

  }

  @Override

  public int getCount() {
    return (10);

  }

  @Override

  public Fragment getItem(int position) {

    return (PageFragment.newInstance(position));

  }

  @Override

  public String getPageTitle(int position) {

    return (PageFragment.getTitle(context, position));

  }

}

За допомогою методу getPageTitle () ми можемо повернути заголовок для вкладки в PagerTabStrip. Фактично в ньому ми звертаємося до методу

getTitle () фрагмента для установки заголовка.

І в кінці трохи підредагуємо код MainActivity, змінивши його метод onCreate:

package com.example.viewpagerapp;

import android.support.v4.view.ViewPager;
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);

    ViewPager pager = (ViewPager) findViewById(R.id.pager);
    pager.setAdapter(new MyAdapter(this, getSupportFragmentManager()));

  }

}

Все інше залишається без змін. Запустимо проект на виконання і побачимо інтерактивні вкладки-заголовки поверх сторінок.


© 2006—2023 СумДУ