Karena pertanyaan Anda mengatakan "Bagaimana cara menggunakan RecyclerView
dengan database" dan Anda tidak spesifik apakah Anda ingin SQLite atau yang lainnya RecyclerView
, saya akan memberikan solusi yang sangat optimal. Saya akan menggunakan Realm sebagai basis data dan membiarkan Anda menampilkan semua data di dalam RecyclerView
. Ini memiliki dukungan permintaan asinkron juga tanpa menggunakan Loaders
atau AsyncTask
.
Mengapa dunia?
Langkah 1
Tambahkan dependensi gradle untuk Realm, ketergantungan untuk versi terbaru dapat ditemukan di sini
Langkah 2
Buat kelas model Anda, misalnya, katakanlah sesuatu yang sederhana seperti Data
yang memiliki 2 bidang, string yang akan ditampilkan di dalamRecyclerView
baris dan cap waktu yang akan digunakan sebagai itemId untuk memungkinkan RecyclerView
item bernyawa. Perhatikan bahwa saya memperluas di RealmObject
bawah ini karena Data
kelas Anda akan disimpan sebagai tabel dan semua properti Anda akan disimpan sebagai kolom dari tabel itu Data
. Saya telah menandai teks data sebagai kunci utama dalam kasus saya karena saya tidak ingin string ditambahkan lebih dari sekali. Tetapi jika Anda lebih suka memiliki duplikat, maka jadikan timestamp sebagai @PrimaryKey. Anda bisa memiliki tabel tanpa kunci utama tetapi itu akan menyebabkan masalah jika Anda mencoba memperbarui baris setelah membuatnya. Kunci primer gabungan pada saat penulisan jawaban ini tidak didukung oleh 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;
}
}
Langkah 3
Buat tata letak Anda untuk bagaimana satu baris seharusnya muncul di dalam RecyclerView
. Tata letak untuk item baris tunggal di dalam kami Adapter
adalah sebagai berikut
<?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>
Perhatikan bahwa saya telah menyimpan FrameLayout
root meskipun saya punyaTextView
di dalam. Saya berencana untuk menambahkan lebih banyak item dalam tata letak ini dan karenanya membuatnya fleksibel untuk saat ini :)
Untuk orang-orang yang penasaran di luar sana, ini adalah bagaimana satu item terlihat saat ini.
Langkah 4
Buat RecyclerView.Adapter
implementasi Anda . Dalam hal ini, objek sumber data adalah objek khusus yang disebut RealmResults
yang pada dasarnya adalah LIVE ArrayList
, dengan kata lain, karena item ditambahkan atau dihapus dari tabel Anda, RealmResults
objek ini diperbarui secara otomatis.
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);
}
}
}
Perhatikan bahwa saya menelepon notifyItemRemoved
dengan posisi di mana penghapusan terjadi tetapi saya TIDAK menelepon notifyItemInserted
atau notifyItemRangeChanged
karena tidak ada cara langsung untuk mengetahui posisi mana item yang dimasukkan ke dalam basis data karena entri Realm tidak disimpan dengan cara yang dipesan. The RealmResults
objek auto update setiap kali item baru ditambahkan, dimodifikasi atau dihapus dari database sehingga kita sebut notifyDataSetChanged
sambil menambahkan dan memasukkan entri massal. Pada titik ini, Anda mungkin khawatir tentang animasi yang tidak akan dipicu karena Anda menelepon notifyDataSetChanged
di tempat notifyXXX
metode. Itulah sebabnya saya memiliki getItemId
metode mengembalikan timestamp untuk setiap baris dari objek hasil. Animasi dicapai dalam 2 langkah dengan notifyDataSetChanged
jika Anda memanggil setHasStableIds(true)
dan kemudian menimpanyagetItemId
untuk memberikan sesuatu selain dari posisi.
Langkah 5
Mari kita tambahkan RecyclerView
ke Activity
atau Fragment
. Dalam kasus saya, saya menggunakan Activity
. File layout yang berisi RecyclerView
sangat sepele dan akan terlihat seperti ini.
<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" />
Saya telah menambahkan app:layout_behavior
sejak saya RecyclerView
masuk ke dalam CoordinatorLayout
yang saya belum diposting di jawaban ini untuk singkatnya.
Langkah 6
Bangun RecyclerView
kode in dan berikan data yang dibutuhkan. Buat dan inisialisasi objek Realm di dalamnya onCreate
dan tutup di dalamnya onDestroy
seperti menutup SQLiteOpenHelper
instance. Paling sederhana onCreate
di dalam Anda Activity
akan terlihat seperti ini. The initUi
metode adalah di mana semua keajaiban terjadi. Saya membuka contoh Realm di dalam 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);
}
Perhatikan bahwa pada langkah pertama, saya meminta Realm untuk memberi saya semua objek dari Data
kelas yang diurutkan berdasarkan nama variabel mereka yang disebut data secara asinkron. Ini memberi saya RealmResults
objek dengan 0 item pada utas utama yang saya atur di Adapter
. Saya menambahkan RealmChangeListener
untuk diberitahu ketika data telah selesai memuat dari utas latar tempat saya menelepon notifyDataSetChanged
dengan saya Adapter
. Saya juga telah memanggil setHasStableIds
true untuk membiarkan RecyclerView.Adapter
implementasi melacak item yang ditambahkan, dihapus atau dimodifikasi. Untuk onDestroy
saya Activity
menutup instance Realm
@Override
protected void onDestroy() {
super.onDestroy();
mRealm.close();
}
Metode ini initUi
dapat disebut dalam onCreate
dari Anda Activity
atau onCreateView
atau onViewCreated
dari Anda Fragment
. Perhatikan hal-hal berikut.
Langkah 7
BAM! Ada data dari database dalam Anda RecyclerView
asynchonously dimuat tanpa CursorLoader
, CursorAdapter
, SQLiteOpenHelper
dengan animasi. Gambar GIF yang ditampilkan di sini agak lamban tetapi animasinya terjadi ketika Anda menambahkan item atau menghapusnya.