Поскольку в вашем вопросе говорится «Как использовать RecyclerViewс базой данных», и вы не уточняете, хотите ли вы SQLite или что-то еще с ним RecyclerView, я дам вам решение, которое является оптимальным. Я буду использовать Realm в качестве базы данных и позволю вам отображать все данные внутри вашего RecyclerView. Он также поддерживает асинхронные запросы без использования Loadersили AsyncTask.
Почему царство?

Шаг 1
Добавьте зависимость Gradle для Realm, зависимость для последней версии находится здесь
Шаг 2
Например, создайте свой класс модели, скажем что-то простое, например, у Dataкоторого есть 2 поля, строка, которая будет отображаться внутри RecyclerViewстроки, и отметка времени, которая будет использоваться как itemId для разрешения RecyclerViewанимации элементов. Обратите внимание, что я расширяю RealmObjectниже, из-за чего ваш Dataкласс будет храниться как таблица, а все ваши свойства будут храниться как столбцы этой таблицы Data. В моем случае я пометил текст данных как первичный ключ, так как не хочу, чтобы строка добавлялась более одного раза. Но если вы предпочитаете иметь дубликаты, сделайте отметку времени как @PrimaryKey. У вас может быть таблица без первичного ключа, но это вызовет проблемы, если вы попытаетесь обновить строку после ее создания. Составной первичный ключ на момент написания этого ответа Realm не поддерживает.
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
public class Data extends RealmObject {
@PrimaryKey
private String data;
//The time when this item was added to the database
private long timestamp;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
Шаг 3
Создайте свой макет для того, как одна строка должна отображаться внутри RecyclerView. Макет для элемента с одной строкой внутри нашего Adapterвыглядит следующим образом
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/white"
android:padding="16dp"
android:text="Data"
android:visibility="visible" />
</FrameLayout>
Обратите внимание, что я сохранил FrameLayoutкорневую папку, хотя у меня есть TextViewвнутренняя. Я планирую добавить больше элементов в этот макет и, следовательно, пока сделал его гибким :)
Для любопытных вот как сейчас выглядит отдельный предмет.

Шаг 4
Создайте свою RecyclerView.Adapterреализацию. В этом случае объект источника данных - это специальный вызываемый объект, RealmResultsкоторый по сути является LIVE ArrayList, другими словами, когда элементы добавляются или удаляются из вашей таблицы, этот RealmResultsобъект обновляется автоматически.
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import io.realm.Realm;
import io.realm.RealmResults;
import slidenerd.vivz.realmrecycler.R;
import slidenerd.vivz.realmrecycler.model.Data;
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.DataHolder> {
private LayoutInflater mInflater;
private Realm mRealm;
private RealmResults<Data> mResults;
public DataAdapter(Context context, Realm realm, RealmResults<Data> results) {
mRealm = realm;
mInflater = LayoutInflater.from(context);
setResults(results);
}
public Data getItem(int position) {
return mResults.get(position);
}
@Override
public DataHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.row_data, parent, false);
DataHolder dataHolder = new DataHolder(view);
return dataHolder;
}
@Override
public void onBindViewHolder(DataHolder holder, int position) {
Data data = mResults.get(position);
holder.setData(data.getData());
}
public void setResults(RealmResults<Data> results) {
mResults = results;
notifyDataSetChanged();
}
@Override
public long getItemId(int position) {
return mResults.get(position).getTimestamp();
}
@Override
public int getItemCount() {
return mResults.size();
}
public void add(String text) {
//Create a new object that contains the data we want to add
Data data = new Data();
data.setData(text);
//Set the timestamp of creation of this object as the current time
data.setTimestamp(System.currentTimeMillis());
//Start a transaction
mRealm.beginTransaction();
//Copy or update the object if it already exists, update is possible only if your table has a primary key
mRealm.copyToRealmOrUpdate(data);
//Commit the transaction
mRealm.commitTransaction();
//Tell the Adapter to update what it shows.
notifyDataSetChanged();
}
public void remove(int position) {
//Start a transaction
mRealm.beginTransaction();
//Remove the item from the desired position
mResults.remove(position);
//Commit the transaction
mRealm.commitTransaction();
//Tell the Adapter to update what it shows
notifyItemRemoved(position);
}
public static class DataHolder extends RecyclerView.ViewHolder {
TextView area;
public DataHolder(View itemView) {
super(itemView);
area = (TextView) itemView.findViewById(R.id.area);
}
public void setData(String text) {
area.setText(text);
}
}
}
Обратите внимание, что я звоню notifyItemRemovedс позиции, в которой произошло удаление, но Я НЕ звоню, notifyItemInsertedили notifyItemRangeChangedпотому что нет прямого способа узнать, в какую позицию элемент был вставлен в базу данных, поскольку записи Realm не хранятся в упорядоченном виде. RealmResultsОбъект автоматически обновляется каждый раз , когда новый элемент добавляется, изменен или удален из базы данных , поэтому мы называем в notifyDataSetChangedто время как добавление и вставкой записей сыпучими. На этом этапе вас, вероятно, беспокоят анимации, которые не будут запускаться из-за того, что вы вызываете notifyDataSetChangedвместо notifyXXXметодов. Именно поэтому у меня getItemIdметод возвращает временную метку для каждой строки из объекта результатов. Анимация достигается в 2 этапа, notifyDataSetChangedесли вы вызываете, setHasStableIds(true)а затем отменяетеgetItemId предоставить что-то иное, чем просто должность.
Шаг 5
Давайте добавим RecyclerViewк нашему Activityили Fragment. В моем случае я использую Activity. Файл макета, содержащий RecyclerViewэлемент, довольно тривиален и будет выглядеть примерно так.
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/text_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
Я добавил, app:layout_behaviorтак как я RecyclerViewперешел внутрь, CoordinatorLayoutкоторый я не публиковал в этом ответе для краткости.
Шаг 6
Создайте RecyclerViewкод и предоставьте необходимые данные. Создайте и инициализируйте объект Realm внутри onCreateи закройте его внутри onDestroyпочти так же, как закрытие SQLiteOpenHelperэкземпляра. В самом простом случае ваша onCreateвнутренняя часть Activityбудет выглядеть так. В initUiметоде происходит все волшебство. Я открываю экземпляр Realm внутри onCreate.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRealm = Realm.getInstance(this);
initUi();
}
private void initUi() {
//Asynchronous query
RealmResults<Data> mResults = mRealm.where(Data.class).findAllSortedAsync("data");
//Tell me when the results are loaded so that I can tell my Adapter to update what it shows
mResults.addChangeListener(new RealmChangeListener() {
@Override
public void onChange() {
mAdapter.notifyDataSetChanged();
Toast.makeText(ActivityMain.this, "onChange triggered", Toast.LENGTH_SHORT).show();
}
});
mRecycler = (RecyclerView) findViewById(R.id.recycler);
mRecycler.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new DataAdapter(this, mRealm, mResults);
//Set the Adapter to use timestamp as the item id for each row from our database
mAdapter.setHasStableIds(true);
mRecycler.setAdapter(mAdapter);
}
Обратите внимание, что на первом этапе я запрашиваю Realm, чтобы предоставить мне все объекты из Dataкласса, отсортированные по имени их переменной, называемой данными, асинхронным образом. Это дает мне RealmResultsобъект с 0 элементами в основном потоке, который я устанавливаю в Adapter. Я добавил, RealmChangeListenerчтобы получать уведомления о завершении загрузки данных из фонового потока, в который я вызываю notifyDataSetChangedсвой Adapter. Я также вызвал setHasStableIdsзначение true, чтобы позволить RecyclerView.Adapterреализации отслеживать элементы, которые добавляются, удаляются или изменяются. The onDestroyfor my Activityзакрывает экземпляр Realm
@Override
protected void onDestroy() {
super.onDestroy();
mRealm.close();
}
Этот метод initUiможно вызвать внутри onCreateвашего Activityили onCreateViewили onViewCreatedвашего Fragment. Обратите внимание на следующие вещи.
Шаг 7
BAM! Существуют данные из базы данных внутри вашей RecyclerViewasynchonously загружены без CursorLoader, CursorAdapter, SQLiteOpenHelperс анимацией. Показанное здесь изображение в формате GIF немного тормозит, но анимация появляется, когда вы добавляете или удаляете элементы.
