Android studio recyclerview sqlite

Использование простой базы данных SQLite в Android-приложении

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

Что такое SQLite

SQLite — это система управления реляционными базами данных, похожая на Oracle , MySQL , PostgreSQL и SQL Server . Она реализует большую часть стандарта SQL , но в отличие от четырех упомянутых выше СУБД она не поддерживает модель « клиент-сервер ». Скорее, она встроена в конечную программу. Это означает, что можно связать базу данных SQLite с приложением и получить доступ ко всем возможностям БД в своем приложении.

Данная СУБД совместима как с Android , так и с iOS , и каждое приложение может создавать и использовать базу данных SQLite . В Android контакты и медиа хранятся и ссылаются на БД SQLite . Она является наиболее используемой СУБД в мире и самым распространенным программным обеспечением . Чтобы узнать о базах данных SQLite как можно больше, посетите официальный сайт SQLite .

Подготовка

Чтобы включить привязку данных в приложении, нужно добавить в файл build.gradle следующий код:

Чтобы использовать как RecyclerView , так и CardView для отображения списков, нужно включить соответствующие библиотеки в разделе зависимостей в файле build.gradle :

Чтобы задействовать все возможности базы данных SQLite , лучше изучить синтаксис SQL .

Описание примера приложения

В нашем Android SQLite примере мы создадим две таблицы: Employer и Employee . Таблица Employee будет содержать ссылку на внешний ключ таблицы Employer . Мы рассмотрим, как вставлять, выбирать, обновлять и удалять строки из таблиц. Я также продемонстрирую, как вывести элементы, выбранные из базы данных SQLite в RecyclerView ( список ) и в Spinner .

У нас есть MainActivity , из которого можно перейти к EmployerActivity ( для работы с таблицей Employer ) или к EmployeeActivity ( для работы с таблицей Employee ):

Классы хранения базы данных SQLite

Классы определяют то, как данные хранятся в базе. SQLite сохраняют значения с помощью пяти доступных классов хранения:

  • NULL — нулевое значение;
  • INTEGER — для целых чисел, содержащих от 1 до 8 байтов;
  • REAL — числа с плавающей запятой;
  • TEXT — текстовые строки, хранящиеся с использованием кодировки базы данных ( UTF-8 или UTF-16 );
  • BLOB — двоичные данные, хранящиеся точно так, как они были введены.

Определение таблиц

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

Начнем с Android SQLite query создания таблицы Employer , а затем перейдем к EmployerActivity .

Рекомендуется размещать логику создания базы х SQLite в классе. Это облегчает устранение возможных неполадок. Назовем наш класс SampleDBContract :

Мы определяем частный конструктор для SampleDBContract , а затем создаем класс для представления таблицы Employer . Обратите внимание: класс Employer реализует интерфейс BaseColumns . Он предоставляет два столбца в нашей таблице. Это столбец _ID , который будет автоматически увеличиваться при добавлении каждой новой строки. И столбец _COUNT , который может использоваться ContentProviders для возврата количества записей, извлекаемых через запрос. Столбец _COUNT не является обязательным. Строка CREATE_TABLE компилируется в следующий оператор SQL :

На данный момент в нашем Android SQLite примере мы определили схему таблицы Employer .

Создание базы данных с помощью SQLiteOpenHelper

Самый простой способ управления созданием базы данных и версиями — создать подкласс SQLiteOpenHelper . Он упрощает управление базой данных SQLite , создавая БД, если они не существуют. Необходимо только переопределить методы onCreate() и onUpgrade() , чтобы указать нужное действие для создания или обновления базы данных:

Теперь в нашем примере Android database SQLite задаем для нашей базы данных SQLite имя ( sample_database ). Конструктор вызывает конструктор суперкласса с именем и версией базы данных. В onCreate мы указываем объекту SQLiteDatabase выполнить оператор Employer CREATE_TABLE SQL . Через onUpgrade мы сбрасываем таблицу Employer и создаем ее снова:

Таблица Employer имеет три столбца: name , description и founded_date . Нажатие кнопки сохранения вызывает метод saveToDB() :

В saveToDB() мы получаем ссылку на объект SQLiteDatabase , используя метод getWritableDatabase() из SQLiteOpenHelper . Этот метод создает базу данных, если она еще не существует, или открывает ее, если она уже создана. GetWritableDatabase возвращает объект SQLiteDatabase , который открывает доступ на чтение / запись:

В приведенном выше фрагменте кода есть четыре момента:

  1. Мы получаем объект SQLiteDatabase , который открывает доступ на запись в базу данных;
  2. Значения, которые будут храниться в базе данных, помещаются в объект ContentValue с именем столбца в качестве ключа;
  3. Мы помещаем Date в объект ContentValue , который переводится в класс хранения данных Android SQLite INTEGER ;
  4. При вставке строки в базу данных с помощью метода database.insert() возвращается идентификатор строки.

Выбор данных из базы данных SQLite

Подобно тому, как мы применили метод getWritableDatabase() , можно вызвать getReadableDatabase() объекта SQLiteOpenHelper для получения объекта SQLiteDatabase , который можно использовать для чтения информации из базы данных. Стоит отметить, что объект SQLiteDatabase , возвращаемый getReadableDatabase() , предоставляет собой тот же самый доступ на чтение / запись в базу данных, который был возвращен функцией getWritableDatabase() , за исключением тех случаев, когда существуют определенные ограничения. Например, файловая система, содержащая заполненную базу данных, и база данных может быть открыта только для чтения.

Читайте также:  Смайлик ниндзя вконтакте андроид

Метод readFromDB будет запрашивать БД, и возвращать все строки из таблицы Employer , в которых имя или описание из таблицы Employer совпадает со значением, введенным в EditText . А также строки, в которых дата основания компании совпадает со значением, введенным в EditText :

В коде Android SQLite query , приведенного выше, projection является массивом String , представляющим столбцы, которые мы хотим получить. selection является строковым представлением условия SQL WHERE , отформатированным таким образом, что символ ‘?’ будет заменен аргументами в массиве selectionArgs String . Вы также можете группировать, фильтровать и сортировать результаты запроса. Вставка данных в базу SQLite с использованием описанного выше метода защищает от SQL-инъекций .

Обратите внимание на объект, возвращаемый запросом — Cursor . В следующем разделе мы покажем, как вывести содержимое Cursor с помощью RecyclerView .

Отображение содержимого объекта Cursor в RecyclerView

Cursor предоставляет произвольный доступ к набору результатов, возвращаемому запросом к базе данных. Это означает, что через Cursor можно получить доступ к значениям в любом месте, подобно Java-спискам или массивам. Благодаря этому приему можно реализовать RecyclerView с использованием Cursor так же, как мы реализуем RecyclerView с помощью ArrayLists . Вместо вызова List.get(i) , вы перемещаете Cursor в нужную позицию, используя moveToPosition() . После этого вызываете соответствующий метод getXXX(int columnIndex) , где XXX — это Blob , Double , Float , Int , Long , Short или String .

Чтобы не беспокоиться о корректных индексах столбцов из метода readFromDB() , примененного выше, мы используем метод getColumnIndexOrThrow() , который извлекает индекс указанного столбца или генерирует исключение, если имя столбца не существует внутри объекта Cursor :

Определение внешних ключей

На данный момент в нашем Android SQLite примере мы создали таблицу Employer , которую заполнили строками. Теперь создадим таблицу Employee , которая связана с таблицей Employer через столбец _ID Employer . Мы определяем класс Employee , который расширяет BaseColumns в классе SampleDBContract . Обратите внимание, что при создании таблицы Employee использовали « FOREIGN KEY(employer_id) REFERENCES employer(_id) «:

Обновление SQLiteOpenHelper

На данный момент в Android Studio SQLite у вас должна быть создана таблица Employer и в нее добавлены значения. Если вы не изменяете версию базы данных, новая таблица Employee не будет создана. К сожалению, если вы измените версию через повторный вызов метода onUpgrade() , то таблица Employer будет сброшена. Чтобы предотвратить это, можно закомментировать или удалить оператор drop в методе onUpgrade() и добавить оператор execSQL() для создания таблицы Employee . Поскольку таблица Employee ссылается на таблицу Employer , сначала необходимо создать таблицу Employer :

Отображение данных из запроса SQLite в Spinner

Чтобы создать работника в таблице Employee , пользователю необходимо выбрать соответствующего работодателя в таблице Employer . Для этого можно предоставить пользователю Spinner . Отобразить содержимое Cursor в Spinner довольно просто.

Сначала мы выполняем Android SQLite query , как было описано выше, выбираем только name из Employer и id (queryCols) . Затем создаем экземпляр SimpleCursorAdapter , передавая ему Cursor , массив столбцов для отображения ( adapterCols ) и массив представлений, с помощью которых должны отображаться столбцы ( adapterRowViews ). Затем устанавливаем Spinner Adapter для SimpleCursorAdapter :

Вставка внешнего ключа в базу данных

Вставка строки, содержащей внешний ключ, полностью идентична вставке строк в таблицу без ограничений по внешнему ключу. Разница заключается в том, что в Android SQLite примере мы получаем ссылку на выбранный объект Cursor из Spinner , а затем — значение столбца _ID Employer :

Выборка данных из базы SQLite с помощью JOIN

Нельзя использовать метод SQLiteDatabase query() для выполнения запроса к нескольким таблицам. Для этого нужно составить собственный SQL-запрос . В приведенном ниже примере запрос определяется в классе SampleDBContract :

Обратите внимание, что в условии WHERE мы используем символ « ? ». Чтобы не нарушить синтаксис SQL нужно определить selectArgs String [] со значениями, которые будут заменять в предоставленном SQL-запросе символ « ? »:

В заключении

Полная версия исходного кода доступна на github для использования и изменения. Базы данных Android SQLite — это мощное средство, доступное для всех мобильных приложений.

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

Дайте знать, что вы думаете по данной теме в комментариях. За комментарии, отклики, подписки, лайки, дизлайки низкий вам поклон!

Источник

FreshByte Labs

RecyclerView with sqlite database android tutorial

The following gif is an overview of what we are about to build

add, edit, delete, search contacts

First of all create a new project in android studio
now setup the following dependencies in your app level build.gradle file

Now setup all the assets like shown below

go to res >> drawable and create brown_border.xml,pedit.png,pdelete.png

pedit.png(download)
pdelete.png(download)

Now go to res >> values >> colors.xml and add the following

Now go to res >> values >> strings.xml and add the following

Now create res >> menu >> menu_main.xml like this

Now create res >> layout and add the following

activity_main.xml add_contact_layout.xml contact_list_layout.xml
Now let us look into some java code
First of all create a helper class which extends SQLiteOpenHelper as shown below

SqliteDatabase.java
The above helper class provides methods to perform all the basic CRUD operations like add, update, delete etc.Now we should create a model class for the contacts object, it should be like

Читайте также:  Джойстик для smart tv android

Contacts.java
Now we should create a recyclerview adapter for our recyclerview. It will include a Filter method which would be used to do search operations within our contacts list.

Below is the code for ContactViewHolder.java which is used in the above adapter class
Update your MainActivity.java file like shown below
With this, we have come to the end of this tutorial, now try running the app!

24 comments:

can you specify the error that you encountered

Источник

ziginsider

25 февраля 2017

Заметки о RecyclerView. Принципы работы. Внутреннее устройство.

Оглавление

Принцип работы RecyclerView в общих словах

Прокручивается список, создаются вьюхи и выводятся на экран, при этом выполняется onCreateViewHolder() и onBindViewHolder() .

Ушедшие за экран вьюхи не уничтожаются, а попадают в пул объектов Recycled Pool .

При дальнейшем скролле, вьюхи появляющиеся из-за пределов экрана не пересоздаются, а берутся из этого самого пула . При этом срабатывает только onBindViewHolder() .

Говоря отвлеченно, метод onCreateViewHolder() создает “бассейн”, а метод onBindViewHolder() “наполняет бассейн водой”. Если каждый раз, когда меняется представление (скролл) не “менять воду в бассейне” полностью т.е. не переопределять содержание всех элементов, которые могут измениться, в onBindViewHolder() , то вьюха может выдавать сюрпризы в виде старых значений.

Компоненты RecyclerView

  • LayoutManager — размещает элементы
  • ItemAnimator — анимирует элементы
  • Adapter — создает элементы
  • Decorator — дорисовывает элементы
  • ViewHolder — кеширует findViewById

LayoutManager

  • LinearLayoutManager (линейное размещение элементов)
  • GridLayoutManager (табличное)
  • StaggeredGridLayoutManager (сложное)


Обязаности LayoutManager’а:

  • размещает элементы
  • отвечает за размер элементов (measure)
  • отвечает за то, какие элементы больше не нужны
  • отвечает за скроллинг
  • отвечает за View Focusing (В случае ListView отвечал сам ListView ); т.е. на каком элементе сфокусироваться.
  • отвечает за Accessibility; для людей с ограниченными возможностями.

Что делать, если стандартная реализация не подходит? Два варианта:

  • Переопределить стандартный LayoutManager:
    Например, у нас есть LinearLayoutManager. Нам необходимо сделать так, что при малом количестве элементов, наш LinearManager будет по высоте занимать ровно столько места, сколько нужно под это количество элементов. Тогда делаем свой ExtendedLinearLayoutManager, где переопределяем метод onMeasure().
  • Написать свой LayoutManager:
    Достаточно переопределить (для компиляции) метод RecyclerView.LayoutParams generateDefaultLayoutParams(…), который будет говорить, какие по умолчанию параметры должны быть применены к новым View.
    Не стоит забывать также void onLayoutChildren(…) — непосредственное размещение наших дочерних View на экране.
    boolean canScrollHorizontally(…) — говорим можем ли мы листать горизонтально
    boolean canScrollVertically(…) — вертикально…

Adapter

Обязанности Adapter’а:

  • создание ViewHolder’ов
  • заполнение ViewHolder’ов информацией
  • уведомление RecyclerView о том какие элементы изменились.
  • обработка касаний
  • частичное обновление данных
  • управление количеством ViewType’ов
  • информация о переиспользовании ViewHolder’а

Основное API Adapter’a:

При изменении позиции элемента не вызывается, поэтому нельзя вызывать так: см. типичные ошибки #1

Методы notifyItemX

Нужны для того, чтобы изменять, удалять, добавлять элементы и при этом анимировать их:

польза от методов notifyItemX():

  • Нет лишних вызовов onBindViewHolder() ;
  • Возможность анимировать и перемещать элементы как угодно
  • Нет лишних вызовов onCreateViewHolder()
  • если в конструкторе вызвать этот метод с true, то RecyclerView сам будет вычислять, какие элементы поменялись местами, какие добавились, удалились и т.д. Для этого необходимо реализовать:

и давать уникальное Id элемента на основе его содержимого или создавать Id на основе Id layout’a, из которого мы надуваем это view, и который всегда уникальный.

ViewHolder

Обязателен, в отличии от реализации ListView. (NB: см. отличия RecyclerView от ListView в этой заметке)

Для чего нужен был ViewHolder раньше? Ответ: кеширование относительно дорогого findViewById

Для чего нужен ViewHolder теперь?

  • кеширование относительно дорогого findViewById
  • Мост между LayoutManager, Animator’ами и Decorator’ами
  • Основной элемент Recycling’а

NB: Как правильно организовать код, когда ViewHolder’ов несколько (несколько типов элементов в одном RecyclerView)? Ответ см. в этой заметке: Различные типы Item View в RecyclerView

Жизнь и смерть ViewHolder

  • LayoutManager дает запрос RecyclerView на элемент: getViewForPosition
  • RecyclerView обращается в Cache
  • Если в Cache нет элемента, RecyclerView идет в Adapter и просит у него viewType
  • Получив ViewType RecyclerView идет в Recycler Pool: getViewHolderByType, в котором может хранится элемент с неправильными данными, которые надо потом заполнить.
  • Если Recycler Pool не вернул элемент, он идет в Adapter и просит последнего создать элемент.
  • Если Recycler Pool вернул элемент, то RecyclerView просит Adapter сделать bindViewHolder (заполнить данные) и возвращает элемент LayoutManager’у

В случае, если LayoutManager добавляет элемент:

  • LayoutManager сообщает RecyclerView о том, что создает элемент: addView
  • RecyclerView посылает запрос Adapter’у: onViewAttachedToWindow

В случае удаления элемента:

  • LayoutManager сообщает RecyclerView о том, что удаляет элемент: removeAndRecycleView
  • RecyclerView посылает запрос Adapter’у: onViewDettachedToWindow
  • RecyclerView идет в Cache, если элемент валидный (isValid? А если не valid то сразу идет в Recycler Pool) для этой позиции, помещает туда элемент
  • Cache помещает старые элементы в Recycler Pool (recycle)
  • Recycled Pool сообщает Adapter’у, что элемент (view) был переиспользован и Adapter может сделать с ним что хочет: onViewRecycled (те элементы которые попадают в Recycled Pool, понадобятся нескоро, в отличии от тех, которые находятся в Cache)

Удаление из представления с т.з. RecyclerView:
RecyclerView при удалении своего child из представления (прокрутка, удаление элемента) view не удаляет ее сразу, но помещает в re add to ViewGroup, скрывает view от LayoutManager’а (иначе LM упадет с exeption, т.к. он занет только о видимых элементах) и говорит ItemAnimator’у, что ее надо анимировать.

Удаление из представления с т.з. LayoutManager’а:
Если LayoutManager удаляет элемент из представления он посылает запрос к RecyclerView removeAndRecycleView. RecyclerView проверяет view на валидность (isValid?) NO! -> отправляет view в Recycler Pool. Recycled Pool проверяет элемент на транзиентность hasTransientState? – состояние (на системном уровне, а не уровне RecyclerView) когда не ясно в каком состоянии view (например, анимация или selection text). И тут (hasTransientState = true) выходит на сцену Adapter. У него есть возможность последний раз сказать RecyclerView, что view можно переиспользовать. И если он скажет, что можно, то recycle’м его, иначе – навсегда его теряем (отсюда вывод: единственный, кто может анимировать view – ItemAnimator. Остальное на свой страх и риск: можно потерять ViewHolder)

Читайте также:  Умные устройства для андроидов

NB: как проверить список на ошибки (торможнение, неправильное использование hasTransientState и глюки из-за этого). Открываем дебажный лог и начинаем прокручивать список. Если видим, что потребление памяти растет, то у нас ошибка в архитектуре Recycler’а (ViewHolder не освобождается)

Еще один способ умереть ViewHolder’у. RecyclerView обращается к Recycled Pool: addViewToPool. Recycled Pool проверяет есть ли место для еще одного ViewHolder’а типа Х. Если место есть, то ОК. Иначе, ViewHolder умирает.

Почему места может не быть?

  • Слишком много ViewHolder’ов одного типа Почему слишком много?
  • Произошла crossfade анимация для всего списка
  • notifyItemRangeChanged(0, getItemCount());
    Как исправить?
  • Правильно вызывать notifyItemChanged
  • pool.setMaxRecycledViews(type, count); (кол-во эл-ов кот. будут кэшиться, важно для тех у кого неоднородные списки, для одних типов (тех кот.больше на экране) можно увеличить значения, для других (тех кот. меньше) – уменьшить)

ItemDecoration

Adapter — для того чтобы представить какой-либо массив данных на экране. ItemDecoration — для того чтобы дополнить это представление в зависимости от какой-либо логики (например, смена ориентации экрана) вашего приложения.

Для чего применяется:

  • добавление разделителей и отступов
  • отображение выбранного элемента (выделение)
  • рисование любого контента за и перед view

onDraw() и onDrawOver() — вызываются на каждый шаг отрисовки списка, поэтому в этих методах не стоит помещать тяжелых операций (инициализация объекта и т.д.)

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

И для избегания таких ситуаций есть замечательные методы:

ItemTouchHelper

Статья призванная показать, как использовать ItemTouchHelper в RecyclerView: Drag and Swipe with RecyclerView

ItemAnimator

ItemAnimator — позволяет анимировать добавление, удаление, изменение элементов.

Схема запуска анимации после методов notifyItemX() (методы см. выше):

Отрисовка элементов делится на два этапа:

  • preLayout — элементы до изменения списка + те, что должны появиться;
  • postLayout — элементы после окончания анимации. RecyclerView запоминает состояние preLayout и сравнивает его с состоянием PostLayout. И определив эту разницу, RecyclerView запускает анимацию элементов.

ExpandableLayout

  • Подход 1 (неправильный): Дерграем requestLayout() для view:

Плохой подход т.к. requestLayout() не предназначен для анимации

  • Подход 2: Кастомный Adapter

RecyclerView и SQLite

На практике довольно часто данные для RecyclerView беруться из базы данных SQLite. Можно, конечно, сначала получать данные из базы данных (с помощью SQLiteCursor) и затем передавать их через конструктор RecyclerView для отображения. Но есть готовое решение:

CursorRecyclerViewAdapter — организует работу RecyclerAdapter поверх курсора. Т.е. вам нужно получить курсор, и без посредников, передать его в CursorRecyclerViewAdapter.

Paging Library — Решение от Google в рамках Android Architecture Components. В данном случае взаимодействует с Room

Типичные ошибки новичка

1) Обработка нажатий на элемент внутри onBindViewHolder(…):

Во-первых, создается объект на каждый Bind. Во-вторых, берется position элемента, которая у него была до onBindViewHolder() . Но элемент с помощью notify() может быть перемещен/удален и его position, следовательно, измениться. Для этой ситуации есть решение, а именно установить слушатель нажатия при создании элемента:

Очень важно проверить на NO_POSITION . Сложно представить, как можно кликнуть на то, у чего нет позиции, но иногда такое происходит. Рекомендация от Google не забывать делать эту проверку.

Но зачем кэшировать (создавать) заранее? Надо создавать, когда в этом есть необходимость.

Практика

Как заполнить RecyclerView данными, когда их много?

Повесьте на RecyclerView слушатель прокрутки и, когда он будет прокручен до последнего элемента (или до, скажем, середины списка, чтобы начать загрузку раньше, чем пользователь достигнет конца списка), подгружайте новую порцию данных.

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

Способ №3:

Для организации постепенной подгрузки можно использовать Pagging Library. Библиотека представлена вместе с Architecture Components (см. заметки по ним). Сам запуск механизма подгрузки аналогичен первым двум способам.

Использовать одну из готовых библиотек (например, BRVAH). Такой способ не всегда может подойти, так как приходится тянуть всю библиотеку в проект.

Как обновить данные в RecyclerView?

Ответ только один: обернуть RecyclerView в SwipeRefreshLayout. Теперь можно применять жест названный в народе “резинка от трусов” т.е. потянуть вниз RecyclerView, отпустить, появиться виджет загрузки и т.д.

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

Как организовать код, когда имеем вложенные в друг друга RecyclerView?

Как организовать код, когда имеем разные типы View в RecyclerView?

Источник

Оцените статью