Как реализовать поиск по SQLite базе данных android приложения во фрагменте
Есть код, предназначенный для поиска по базе данных и вывода результатов поиска в recyclerView:
@AndroidEntryPoint class MainActivity : AppCompatActivity(), SearchView.OnQueryTextListener < private lateinit var binding: ActivityMainBinding private val mainViewModel: MainViewModel by viewModels() private val myAdapter: MyAdapter by lazy < MyAdapter() >override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // val person = Person("Ana", "Brown", 38) // mainViewModel.insertData(person) binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.adapter = myAdapter mainViewModel.readData.observe(this, < myAdapter.setData(it) >) > override fun onCreateOptionsMenu(menu: Menu?): Boolean < menuInflater.inflate(R.menu.main_menu, menu) val search = menu?.findItem(R.id.menu_search) val searchView = search?.actionView as? SearchView searchView?.isSubmitButtonEnabled = true searchView?.setOnQueryTextListener(this) return true >override fun onQueryTextSubmit(query: String?): Boolean < return true >override fun onQueryTextChange(query: String?): Boolean < if(query != null)< searchDatabase(query) >return true > private fun searchDatabase(query: String) < val searchQuery = "%$query%" mainViewModel.searchDatabase(searchQuery).observe(this, < list ->list.let < myAdapter.setData(it) >>) > >
Однако, если реализовать поиск так, что при переключении между вкладками приложения (разными фрагментами), поиск будет в каждой из них. Мне же нужно, чтобы возможность поиска была только в одной вкладке. Проблема в том, что когда я пробую реализовать то же самое внутри фрагмента, получается следующее:
Отслеживать
задан 18 июл 2022 в 17:00
user373855 user373855
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Вы некорректно сформулировали вопрос. Судя по всему у вас такая ситуация — в дефолтном стиле есть ActionBar в активити, в который вы добавили меню. Теперь же пытаетесь добавлять меню в этот ActionBar из фрагмента. Т.е. поиск и БД тут вообще не при чём. По сути вопроса — если хотите делать именно так (ИМХО это не удобно), то посмотрите тут как. Скорее всего вам будет удобнее убрать ActionBar из активити и добавить через Toolbar отдельно в каждый фрагмент. Меню в таком случае программно можно будет через ссылку на Toolbar загружать.
Отслеживать
ответ дан 18 июл 2022 в 18:57
69.8k 9 9 золотых знаков 66 66 серебряных знаков 122 122 бронзовых знака
Как вывести данные с sqlite android kotlin
Кроме создания новой базы данных мы также можем использовать уже существующую. Это может быть более предпочтительно, так как в этом случае база данных приложения уже будет содержать всю необходимую информацию.
Возьмем проект, созданный в предыдущей теме, где у нас была MainActivity, которая выводила список объектов, и UserActivity, которая позволяла добавлять, редактировать и удалять объекты из БД

Для начала создадим базу данных SQLite. В этом нам может помочь такой инструмент как Sqlitebrowser . Он бесплатный и доступен для различных операционных систем по адресу https://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 :

Затем в появившемся окошке выберем пункт src\main\assets и нажмем на Enter для ее добавления в проект::

И затем скопируем в нее нашу базу данных:

В моем случае база данных называется «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 = "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() < File file = new File(DB_PATH); if (!file.exists()) < //получаем локальную бд как поток try(InputStream myInput = myContext.getAssets().open(DB_NAME); // Открываем пустую бд OutputStream myOutput = new FileOutputStream(DB_PATH)) < // побайтово копируем данные byte[] buffer = new byte[1024]; int length; while ((length = myInput.read(buffer)) >0) < myOutput.write(buffer, 0, length); >myOutput.flush(); > 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 androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.view.View; import android.widget.AdapterView; import android.widget.SimpleCursorAdapter; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.widget.ListView; 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 = findViewById(R.id.list); userList.setOnItemClickListener(new AdapterView.OnItemClickListener() < @Override public void onItemClick(AdapterViewparent, 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[]; // создаем адаптер, передаем в него курсор userAdapter = new SimpleCursorAdapter(this, android.R.layout.two_line_list_item, userCursor, headers, new int[], 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 androidx.appcompat.app.AppCompatActivity; import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; 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 delButton; 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 = findViewById(R.id.name); yearBox = findViewById(R.id.year); delButton = findViewById(R.id.deleteButton); saveButton = 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[]); 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 + "=" + userId, null); >else < db.insert(DatabaseHelper.TABLE, null, cv); >goHome(); > public void delete(View view)< db.delete(DatabaseHelper.TABLE, "_id = ?", new String[]); goHome(); > private void goHome() < // закрываем подключение db.close(); // переход к главной activity Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(intent); >>
Вся остальная работа с данными будет той же, чтобы и в прошлых темах:
Работа с базами данных 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 применяется следующая система типов данных:
- INTEGER : представляет целое число, аналог типу int в java
- REAL : представляет число с плавающей точкой, аналог float и double в java
- TEXT : представляет набор символов, аналог String и char в java
- BLOB : представляет массив бинарных данных, например, изображение, аналог типу int в java
Сохраняемые данные должны представлять соответствующие типы в java.
Создание и открытие базы данных
Для создания или открытия новой базы данных из кода Activity в Android мы можем вызвать метод openOrCreateDatabase() . Этот метод может принимать три параметра:
- название для базы данных
- числовое значение, которое определяет режим работы (как правило, в виде константы MODE_PRIVATE )
- необязательный параметр в виде объекта SQLiteDatabase.CursorFactory , который представляет фабрику создания курсора для работы с бд
Например, создание базы данных 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())
Метод db.rawQuery() возвращает объект Cursor, с помощью которого мы можем извлечь полученные данные.
Возможна ситуация, когда в базе данных не будет объектов, и для этого методом query.moveToFirst() пытаемся переместиться к первому объекту, полученному из бд. Если этот метод возвратит значение false, значит запрос не получил никаких данных из бд.
Теперь для работы с базой данных сделаем простейшее приложение. Для этого создадим новый проект.
В файле activity_main.xml определим простейший графический интерфейс:
А в классе MainActivity определим взаимодействие с базой данных:
package com.example.sqliteapp; import androidx.appcompat.app.AppCompatActivity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; 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, UNIQUE(name))"); db.execSQL("INSERT OR IGNORE INTO users VALUES ('Tom Smith', 23), ('John Dow', 31);"); Cursor query = db.rawQuery("SELECT * FROM users;", null); TextView textView = findViewById(R.id.textView); textView.setText(""); while(query.moveToNext())< String name = query.getString(0); int age = query.getInt(1); textView.append("Name: " + name + " Age: " + age + "\n"); >query.close(); db.close(); > >
По нажатию на кнопку здесь вначале создается в базе данных app.db новая таблица users, а затем в нее добавляются два объекта в базу данных с помощью SQL-выражения INSERT.
Далее с помощью выражения SELECT получаем всех добавленных пользователей из базы данных в виде курсора Cursor.
Вызовом query.moveToNext() перемещаемся в цикле while последовательно по всем объектам.
Для получения данных из курсора применяются методы query.getString(0) и query.getInt(1) . В скобках в методы передается номер столбца, из которого мы получаем данные. Например, выше мы добавили вначале имя пользователя в виде строки, а затем возраст в виде числа. Значит, нулевым столбцом будет идти строкое значение, которое получаем с помощью метода getString() , а следующим — первым столбцом идет числовое значение, для которого применяется метод getInt() .
После завершения работы с курсором и базой данных мы закрываем все связанные объекты:
query.close(); db.close();
Если мы не закроем курсор, то можем столкнуться с проблемой утечки памяти.
И если мы обратимся к приложению, то после нажатия на кнопку в текстовое поле будут выведены добавленные данные:
Урок 34. Хранение данных. SQLite
На прошлом уроке мы рассмотрели самый простой способ хранения данных — Preferences. Но способ этот достаточно ограничен и для хранения большого количества структурированных данных неудобен. На этом уроке рассмотрим SQLite. Это база данных с таблицами и запросами — все как в обычных БД.
Для начала, немного теории по взаимодействию приложения и БД.
В приложении, при подключении к БД мы указываем имя БД и версию. При этом могут возникнуть следующие ситуации:
1) БД не существует. Это может быть например в случае первичной установки программы. В этом случае приложение должно само создать БД и все таблицы в ней. И далее оно уже работает с только что созданной БД.
2) БД существует, но ее версия устарела. Это может быть в случае обновления программы. Например новой версии программы нужны дополнительные поля в старых таблицах или новые таблицы. В этом случае приложение должно апдейтить существующие таблицы и создать новые, если это необходимо.
3) БД существует и ее версия актуальна. В этом случае приложение успешно подключается к БД и работает.
Как вы понимаете, фраза «приложение должно» равнозначна фразе «разработчик должен», т.е. это наша задача. Для обработки описанных выше ситуаций нам надо создать класс, являющийся наследником для SQLiteOpenHelper. Назовем его DBHelper. Этот класс предоставит нам методы для создания или обновления БД в случаях ее отсутствия или устаревания.
onCreate — метод, который будет вызван, если БД, к которой мы хотим подключиться – не существует
onUpgrade — будет вызван в случае, если мы пытаемся подключиться к БД более новой версии, чем существующая
Давайте накидаем простое приложение – справочник контактов, которое будет хранить имя и email. Вводить данные будем на экране приложения, а для отображения информации используем логи. Обычно для этого используется List (список) – но мы эту тему пока не знаем. Да и не хочется перегружать приложение. Главное – освоить приемы работы с БД.
Project name: P0341_SimpleSQLite
Build Target: Android 2.3.3
Application name: SimpleSQLite
Package name: ru.startandroid.develop.p0341simplesqlite
Create Activity: MainActivity
Нарисуем экран для ввода записей и очистки таблицы. Открываем main.xml и пишем:
Пара полей для ввода и кнопки добавления записи, вывода существующих записей и очистки таблицы.
Открываем MainActivity.java и пишем:
package ru.startandroid.develop.p0341simplesqlite; import android.app.Activity; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; public class MainActivity extends Activity implements OnClickListener < final String LOG_TAG = "myLogs"; Button btnAdd, btnRead, btnClear; EditText etName, etEmail; DBHelper dbHelper; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.main); btnAdd = (Button) findViewById(R.id.btnAdd); btnAdd.setOnClickListener(this); btnRead = (Button) findViewById(R.id.btnRead); btnRead.setOnClickListener(this); btnClear = (Button) findViewById(R.id.btnClear); btnClear.setOnClickListener(this); etName = (EditText) findViewById(R.id.etName); etEmail = (EditText) findViewById(R.id.etEmail); // создаем объект для создания и управления версиями БД dbHelper = new DBHelper(this); >@Override public void onClick(View v) < // создаем объект для данных ContentValues cv = new ContentValues(); // получаем данные из полей ввода String name = etName.getText().toString(); String email = etEmail.getText().toString(); // подключаемся к БД SQLiteDatabase db = dbHelper.getWritableDatabase(); switch (v.getId()) < case R.id.btnAdd: Log.d(LOG_TAG, "--- Insert in mytable: ---"); // подготовим данные для вставки в виде пар: наименование столбца - значение cv.put("name", name); cv.put("email", email); // вставляем запись и получаем ее ID long rowID = db.insert("mytable", null, cv); Log.d(LOG_TAG, "row inserted, --- Rows in mytable: ---"); // делаем запрос всех данных из таблицы mytable, получаем Cursor Cursor c = db.query("mytable", null, null, null, null, null, null); // ставим позицию курсора на первую строку выборки // если в выборке нет строк, вернется false if (c.moveToFirst()) < // определяем номера столбцов по имени в выборке int idColIndex = c.getColumnIndex("id"); int nameColIndex = c.getColumnIndex("name"); int emailColIndex = c.getColumnIndex("email"); do < // получаем значения по номерам столбцов и пишем все в лог Log.d(LOG_TAG, "ID = " + c.getInt(idColIndex) + ", name = " + c.getString(nameColIndex) + ", email = " + c.getString(emailColIndex)); // переход на следующую строку // а если следующей нет (текущая - последняя), то false - выходим из цикла >while (c.moveToNext()); > else Log.d(LOG_TAG, "0 rows"); c.close(); break; case R.id.btnClear: Log.d(LOG_TAG, "--- Clear mytable: ---"); // удаляем все записи int clearCount = db.delete("mytable", null, null); Log.d(LOG_TAG, "deleted rows count = " + clearCount); break; > // закрываем подключение к БД dbHelper.close(); > class DBHelper extends SQLiteOpenHelper < public DBHelper(Context context) < // конструктор суперкласса super(context, "myDB", null, 1); >@Override public void onCreate(SQLiteDatabase db) < Log.d(LOG_TAG, "--- onCreate database ---"); // создаем таблицу с полями db.execSQL("create table mytable (" + "id integer primary key autoincrement," + "name text," + "email text" + ");"); >@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) < >> >
Куча новых незнакомых слов в коде. Давайте разбираться.
В методе Activity — onCreate мы определяем объекты, присваиваем обработчики и создаем объект dbHelper класса DBHelper для управления БД. Сам класс будет описан ниже.
Далее смотрим метод Activity – onClick, в котором мы обрабатываем нажатия на кнопки.
Класс ContentValues используется для указания полей таблицы и значений, которые мы в эти поля будем вставлять. Мы создаем объект cv, и позже его используем. Далее мы записываем в переменные значения из полей ввода. Затем, с помощью метода getWritableDatabase подключаемся к БД и получаем объект SQLiteDatabase. Он позволит нам работать с БД. Мы будем использовать его методы insert – вставка записи, query – чтение, delete – удаление. У них много разных параметров на вход, но мы пока используем самый минимум.
Далее смотрим, какая кнопка была нажата:
btnAdd – добавление записи в таблицу mytable. Мы заполняем объект cv парами: имя поля и значение. И (при вставке записи в таблицу) в указанные поля будут вставлены соответствующие значения. Мы заполняем поля name и email. id у нас заполнится автоматически (primary key autoincrement). Вызываем метод insert – передаем ему имя таблицы и объект cv с вставляемыми значениями. Второй аргумент метода используется, при вставке в таблицу пустой строки. Нам это сейчас не нужно, поэтому передаем null. Метод insert возвращает ID вставленной строки, мы его сохраняем в rowID и выводим в лог.
btnRead – чтение всех записей из таблицы mytable. Для чтения используется метод query. На вход ему подается имя таблицы, список запрашиваемых полей, условия выборки, группировка, сортировка. Т.к. нам нужны все данные во всех полях без сортировок и группировок — мы используем везде null. Только имя таблицы указываем. Метод возвращает нам объект класса Cursor. Его можно рассматривать как таблицу с данными. Метод moveToFirst – делает первую запись в Cursor активной и заодно проверяет, есть ли вообще записи в нем (т.е. выбралось ли что-либо в методе query). Далее мы получаем порядковые номера столбцов в Cursor по их именам с помощью метода getColumnIndex. Эти номера потом используем для чтения данных в методах getInt и getString и выводим данные в лог. С помощью метода moveToNext мы перебираем все строки в Cursor пока не добираемся до последней. Если же записей не было, то выводим в лог соответствующее сообщение – 0 rows. В конце закрываем курсор (освобождаем занимаемые им ресурсы) методом close, т.к. далее мы его нигде не используем.
btnClear – очистка таблицы. Метод delete удаляет записи. На вход передаем имя таблицы и null в качестве условий для удаления, а значит удалится все. Метод возвращает кол-во удаленных записей.
После этого закрываем соединение с БД методом close.
Класс DBHelper является вложенным в MainActivity и описан в конце кода. Как я уже писал выше, этот класс должен наследовать класс SQLiteOpenHelper.
В конструкторе мы вызываем конструктор суперкласса и передаем ему:
context — контекст
mydb — название базы данных
null – объект для работы с курсорами, нам пока не нужен, поэтому null
1 – версия базы данных
В методе onCreate этого класса мы используем метод execSQL объекта SQLiteDatabase для выполнения SQL-запроса, который создает таблицу. Напомню – этот метод вызывается, если БД не существует и ее надо создавать. По запросу видно, что мы создаем таблицу mytable с полями id, name и email.
Метод onUpgrade пока не заполняем, т.к. используем одну версию БД и менять ее не планируем.
Все сохраним и запустим приложение. Будем работать с БД и смотреть логи, которые покажут, какие методы выполняются, и что в них происходит.
Введем чего-нить в поля ввода и нажмем Add.

— onCreate database —
— Insert in mytable: —
row inserted, >
Мы видим, что вызывался метод onCreate в классе DBHelper, а значит выполнялся скрипт по созданию таблицы. Это произошло потому, что это первый запуск приложения и БД еще не была создана. Теперь БД существует и с ней можно работать.
Далее видим, что вызывался метод вставки записи и вернул ID = 1.
Вставим еще какую-нибудь запись.

— Insert in mytable: —
row inserted, >
На этот раз onCreate не вызывался, т.к. БД уже существует. Вставилась запись с >
Давайте посмотрим содержимое таблицы — нажмем кнопку Read и посмотрим лог:
— Rows in mytable: —
name = John Smith, email = Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.
name = Some body, email = Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.
Мы видим записи, которые вставляли. Тут все верно.
Теперь очистим таблицу — нажмем Clear. Смотрим лог:
— Clear mytable: —
deleted rows count = 2
Удалено две записи, все верно. Если теперь посмотрим содержимое таблицы – кнопка Read:
— Rows in mytable: —
0 rows
В этой теме важно понять, что для работы с БД мы использовали два класса:
— DBHelper, наследующий SQLiteOpenHelper. В его конструкторе мы вызываем конструктор супер-класса и указываем имя и версию БД. Метод getWritableDatabase выполняет подключение к базе данных и возвращает нам объект SQLiteDatabase для работы с ней. Метод close закрывает подключение к БД. В случае, когда БД отсутствует или устарела, класс предоставляет нам самим реализовать создание или обновление в методах onCreate и onUpgrate.
— SQLiteDatabase. Содержит методы для работы с данными – т.е. вставка, обновление, удаление и чтение.
Файл базы можно найти в File Explorer, как и на прошлом уроке. Путь к нему data/data/ru.startandroid.develop.p0341simpelsqlite/databases/myDB.
На следующем уроке продолжим это приложение. Добавим возможность обновления и удаления конкретных записей.
Важное замечание
Я в своих примерах выполняю все операции с базой данных в основном потоке. Я делаю так, чтобы не усложнять урок. Но в реале вам следует использовать для работы с БД отдельный поток, чтобы ваше приложение не тормозило визуально. О том, как это сделать, я пишу в уроках 80-91 и 135-136.
На следующем уроке:
— используем методы query и delete с указанием условия
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
