Da Ihre Frage "Verwendung RecyclerView
mit einer Datenbank" lautet und Sie nicht genau wissen, ob Sie SQLite oder etwas anderes mit dem möchten RecyclerView
, gebe ich Ihnen eine Lösung, die sehr optimal ist. Ich werde Realm als Datenbank verwenden und Sie alle Daten in Ihrem anzeigen lassen RecyclerView
. Es unterstützt auch asynchrone Abfragen, ohne Loaders
oder zu verwenden AsyncTask
.
Warum Reich?
Schritt 1
Fügen Sie die Gradle-Abhängigkeit für Realm hinzu. Die Abhängigkeit für die neueste Version finden Sie hier
Schritt 2
Wenn Sie beispielsweise Ihre Modellklasse erstellen, können Sie beispielsweise Data
zwei Felder, eine Zeichenfolge, die in der RecyclerView
Zeile angezeigt werden soll, und einen Zeitstempel verwenden, der als itemId zum Animieren von RecyclerView
Elementen verwendet wird. Beachten Sie, dass ich weiter RealmObject
unten erläutere, weshalb Ihre Data
Klasse als Tabelle und alle Ihre Eigenschaften als Spalten dieser Tabelle gespeichert werden Data
. Ich habe den Datentext in meinem Fall als Primärschlüssel markiert, da ich nicht möchte, dass eine Zeichenfolge mehr als einmal hinzugefügt wird. Wenn Sie jedoch Duplikate bevorzugen, legen Sie den Zeitstempel als @PrimaryKey fest. Sie können eine Tabelle ohne Primärschlüssel haben, dies kann jedoch zu Problemen führen, wenn Sie versuchen, die Zeile nach dem Erstellen zu aktualisieren. Ein zusammengesetzter Primärschlüssel zum Zeitpunkt des Schreibens dieser Antwort wird von Realm nicht unterstützt.
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;
}
}
Schritt 3
Erstellen Sie Ihr Layout dafür, wie eine einzelne Zeile in der angezeigt werden soll RecyclerView
. Das Layout für ein einzelnes Zeilenelement in unserem Adapter
ist wie folgt
<?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>
Beachten Sie, dass ich eine FrameLayout
als Wurzel behalten habe , obwohl ich eine TextView
innere habe. Ich habe vor, weitere Elemente in dieses Layout aufzunehmen und es daher vorerst flexibel zu gestalten :)
Für die Neugierigen da draußen sieht ein einzelner Artikel derzeit so aus.
Schritt 4
Erstellen Sie Ihre RecyclerView.Adapter
Implementierung. In diesem Fall ist das Datenquellenobjekt ein spezielles Objekt, RealmResults
das als LIVE bezeichnet ArrayList
wird. Mit anderen Worten, wenn Elemente zu Ihrer Tabelle hinzugefügt oder daraus entfernt werden, wird dieses RealmResults
Objekt automatisch aktualisiert.
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);
}
}
}
Beachten Sie, dass ich notifyItemRemoved
mit der Position anrufe, an der die Entfernung stattgefunden hat, aber NICHT anrufe notifyItemInserted
oder notifyItemRangeChanged
weil es keine direkte Möglichkeit gibt, festzustellen, an welcher Position das Element in die Datenbank eingefügt wurde, da Realm-Einträge nicht in geordneter Weise gespeichert werden. Das RealmResults
Objekt wird automatisch aktualisiert, wenn ein neues Element hinzugefügt, geändert oder aus der Datenbank entfernt wird, sodass wir notifyDataSetChanged
beim Hinzufügen und Einfügen von Masseneinträgen aufrufen . An diesem Punkt sind Sie wahrscheinlich besorgt über die Animationen, die nicht ausgelöst werden, weil Sie notifyDataSetChanged
anstelle von notifyXXX
Methoden aufrufen . Genau deshalb muss die getItemId
Methode den Zeitstempel für jede Zeile aus dem Ergebnisobjekt zurückgeben. Die Animation erfolgt in 2 Schritten mit, notifyDataSetChanged
wenn Sie anrufen setHasStableIds(true)
und dann überschreibengetItemId
etwas anderes als nur die Position zu bieten.
Schritt 5
Fügen RecyclerView
wir das zu unserem Activity
oder hinzu Fragment
. In meinem Fall verwende ich eine Activity
. Die Layoutdatei mit dem RecyclerView
ist ziemlich trivial und würde ungefähr so aussehen.
<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" />
Ich habe eine hinzugefügt, app:layout_behavior
seit ich RecyclerView
in eine gehe, CoordinatorLayout
die ich der Kürze halber in dieser Antwort nicht gepostet habe.
Schritt 6
Konstruieren Sie den RecyclerView
In-Code und geben Sie die benötigten Daten an. Erstellen und initialisieren Sie ein Realm-Objekt im Inneren onCreate
und schließen Sie es im Inneren, onDestroy
ähnlich wie beim Schließen einer SQLiteOpenHelper
Instanz. Im einfachsten Fall sieht Ihr onCreate
Inneres Activity
so aus. Die initUi
Methode ist, wo all die Magie passiert. Ich öffne eine Instanz von Realm im Inneren 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);
}
Beachten Sie, dass ich im ersten Schritt Realm abfrage, um mir alle Objekte aus der Data
Klasse zu geben, die asynchron nach ihrem Variablennamen namens data sortiert sind. Dies gibt mir ein RealmResults
Objekt mit 0 Elementen im Hauptthread, das ich auf dem einstelle Adapter
. Ich habe ein hinzugefügt RealmChangeListener
, um benachrichtigt zu werden, wenn das Laden der Daten aus dem Hintergrund-Thread, in dem ich notifyDataSetChanged
mit meinem aufrufe, abgeschlossen ist Adapter
. Ich habe auch setHasStableIds
true aufgerufen , damit die RecyclerView.Adapter
Implementierung Elemente verfolgt, die hinzugefügt, entfernt oder geändert wurden. Das onDestroy
für mich Activity
schließt die Realm-Instanz
@Override
protected void onDestroy() {
super.onDestroy();
mRealm.close();
}
Diese Methode initUi
kann in onCreate
Ihrem Activity
oder onCreateView
oder onViewCreated
von Ihrem aufgerufen werden Fragment
. Beachten Sie die folgenden Dinge.
Schritt 7
BAM! Es gibt Daten aus der Datenbank in Ihrem RecyclerView
asynchonously ohne geladen CursorLoader
, CursorAdapter
, SQLiteOpenHelper
mit Animationen. Das hier gezeigte GIF-Bild ist etwas verzögert, aber die Animationen werden ausgeführt, wenn Sie Elemente hinzufügen oder entfernen.