เนื่องจากคำถามของคุณระบุว่า "วิธีใช้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
วิธีการเป็นที่ที่ทุกมายากลที่เกิดขึ้น onCreate
ฉันเปิดตัวอย่างของภายใน Realm
@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
. ฉันเพิ่ม a RealmChangeListener
เพื่อรับการแจ้งเตือนเมื่อโหลดข้อมูลเสร็จจากเธรดพื้นหลังที่ฉันโทรnotifyDataSetChanged
ด้วยAdapter
ไฟล์. ฉันได้เรียกร้องsetHasStableIds
ให้ true ให้การRecyclerView.Adapter
ใช้งานติดตามรายการที่เพิ่มลบหรือแก้ไข onDestroy
สำหรับฉันActivity
ปิดเช่นอาณาจักร
@Override
protected void onDestroy() {
super.onDestroy();
mRealm.close();
}
วิธีการนี้initUi
สามารถเรียกได้ว่าภายในonCreate
ของคุณActivity
หรือonCreateView
หรือของคุณonViewCreated
Fragment
สังเกตสิ่งต่อไปนี้
ขั้นตอนที่ 7
BAM! มีข้อมูลจากฐานข้อมูลที่อยู่ภายในของคุณRecyclerView
โหลดได้โดยไม่ต้อง asynchonously CursorLoader
, CursorAdapter
, SQLiteOpenHelper
ที่มีภาพเคลื่อนไหว ภาพ GIF ที่แสดงที่นี่ค่อนข้างล่าช้า แต่ภาพเคลื่อนไหวจะเกิดขึ้นเมื่อคุณเพิ่มรายการหรือลบออก