Puisque votre question dit "Comment utiliser RecyclerViewavec une base de données" et que vous ne spécifiez pas si vous voulez SQLite ou quoi que ce soit d'autre avec le RecyclerView, je vais vous donner une solution hautement optimale. J'utiliserai Realm comme base de données et vous laisserai afficher toutes les données dans votre fichier RecyclerView. Il prend également en charge les requêtes asynchrones sans utiliser Loadersni AsyncTask.
Pourquoi royaume?

Étape 1
Ajoutez la dépendance gradle pour Realm, la dépendance pour la dernière version se trouve ici
Étape 2
Créez votre classe de modèle, par exemple, disons quelque chose de simple comme Dataqui a 2 champs, une chaîne à afficher à l'intérieur de la RecyclerViewligne et un horodatage qui sera utilisé comme itemId pour permettre RecyclerViewà l'animation des éléments. Notez que j'étends RealmObjectci - dessous en raison de laquelle votre Dataclasse sera stockée sous forme de table et toutes vos propriétés seront stockées sous forme de colonnes de cette table Data. J'ai marqué le texte de données comme clé primaire dans mon cas car je ne veux pas qu'une chaîne soit ajoutée plus d'une fois. Mais si vous préférez avoir des doublons, créez l'horodatage sous la forme @PrimaryKey. Vous pouvez avoir une table sans clé primaire, mais cela posera des problèmes si vous essayez de mettre à jour la ligne après l'avoir créée. Une clé primaire composite au moment de la rédaction de cette réponse n'est pas prise en charge par 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;
}
}
Étape 3
Créez votre mise en page pour savoir comment une seule ligne doit apparaître dans le RecyclerView. La disposition d'un élément à une seule ligne à l'intérieur de notre Adapterest la suivante
<?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>
Remarquez que j'ai gardé un en FrameLayouttant que racine même si j'ai un TextViewintérieur. Je prévois d'ajouter plus d'éléments dans cette mise en page et donc de la rendre flexible pour le moment :)
Pour les curieux, voici à quoi ressemble actuellement un seul élément.

Étape 4
Créez votre RecyclerView.Adapterimplémentation. Dans ce cas, l'objet de source de données est un objet spécial appelé RealmResultsqui est essentiellement un LIVE ArrayList, en d'autres termes, lorsque des éléments sont ajoutés ou supprimés de votre table, cet RealmResultsobjet se met à jour automatiquement.
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);
}
}
}
Notez que notifyItemRemovedj'appelle avec la position à laquelle la suppression a eu lieu mais je n'appelle PAS notifyItemInsertedou notifyItemRangeChangedparce qu'il n'y a pas de moyen direct de savoir à quelle position l'élément a été inséré dans la base de données puisque les entrées de royaume ne sont pas stockées de manière ordonnée. L' RealmResultsobjet se met à jour automatiquement chaque fois qu'un nouvel élément est ajouté, modifié ou supprimé de la base de données, nous l'appelons notifyDataSetChangedlors de l'ajout et de l'insertion d'entrées en bloc. À ce stade, vous êtes probablement préoccupé par les animations qui ne seront pas déclenchées car vous appelez notifyDataSetChangedà la place des notifyXXXméthodes. C'est exactement pourquoi la getItemIdméthode renvoie l'horodatage de chaque ligne à partir de l'objet de résultats. L'animation est réalisée en 2 étapes avec notifyDataSetChangedsi vous appelez setHasStableIds(true)puis remplacezgetItemId pour fournir autre chose que juste la position.
Étape 5
Ajoutons le RecyclerViewà notre Activityou Fragment. Dans mon cas, j'utilise un Activity. Le fichier de mise en page contenant le RecyclerViewest assez simple et ressemblerait à quelque chose comme ça.
<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" />
J'ai ajouté un app:layout_behaviordepuis mon RecyclerViewentrée dans un CoordinatorLayoutque je n'ai pas posté dans cette réponse par souci de concision.
Étape 6
Construisez le RecyclerViewcode in et fournissez les données dont il a besoin. Créez et initialisez un objet Realm à l'intérieur onCreateet fermez-le à l'intérieur onDestroyun peu comme la fermeture d'une SQLiteOpenHelperinstance. Au plus simple, votre onCreateintérieur Activityressemblera à ceci. La initUiméthode est l'endroit où toute la magie se produit. J'ouvre une instance de Realm à l'intérieur 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);
}
Notez que dans la première étape, je demande à Realm de me donner tous les objets de la Dataclasse triés par leur nom de variable appelé data de manière asynchrone. Cela me donne un RealmResultsobjet avec 0 éléments sur le thread principal que je mets sur le Adapter. J'ai ajouté un RealmChangeListenerpour être averti lorsque les données ont fini de charger à partir du fil d'arrière-plan où j'appelle notifyDataSetChangedavec mon Adapter. J'ai également appelé setHasStableIdstrue pour permettre à l' RecyclerView.Adapterimplémentation de suivre les éléments ajoutés, supprimés ou modifiés. Le onDestroyfor my Activityferme l'instance Realm
@Override
protected void onDestroy() {
super.onDestroy();
mRealm.close();
}
Cette méthode initUipeut être appelée à l'intérieur onCreatede votre Activityou onCreateViewou onViewCreatedde votre Fragment. Notez les choses suivantes.
Étape 7
BAM! L' intérieur de votre Il y a des données de base de données RecyclerViewasynchonously chargée sans CursorLoader, CursorAdapter, SQLiteOpenHelperavec des animations. L'image GIF présentée ici est un peu lente, mais les animations se produisent lorsque vous ajoutez des éléments ou les supprimez.
