การใช้ Recyclerview กับฐานข้อมูล


127

ขณะนี้ไม่มีการใช้งาน RecyclerView.Adapter เป็นค่าเริ่มต้น

อาจจะมีการเปิดตัวอย่างเป็นทางการ Google จะเพิ่มให้

เนื่องจากมีการสนับสนุนสำหรับการไม่มีCursorAdapterที่มีRecyclerViewอยู่ในปัจจุบันวิธีการที่เราสามารถใช้RecyclerViewกับฐานข้อมูลหรือไม่ ข้อเสนอแนะใด ๆ ?

คำตอบ:


101

หากคุณกำลังเรียกใช้แบบสอบถามด้วยCursorLoaderและคุณต้องการRecyclerViewแทนที่จะเป็นListView.

คุณสามารถลองใช้CursorRecyclerViewAdapter: CursorAdapterของฉันใน RecyclerView


12
รหัสทำงาน แต่ไม่ใช่ภาพเคลื่อนไหวเมื่อลบและแทรกเนื่องจากเมธอด registerDataSetObserver ของคลาส Cursor ไม่ได้ระบุว่าองค์ประกอบใดเปลี่ยนแปลงอย่างแน่นอน ดังนั้นทุกครั้งที่ผู้ให้บริการเนื้อหาทำการเปลี่ยนแปลงRecyclerview จะถูกโหลดอย่างสมบูรณ์ผ่านทางalertDataSetChangedและนี่คือสาระสำคัญของ RecyclerView
francas

1
อะไรคือข้อเสียเปรียบเพียงแค่ส่งเคอร์เซอร์ไปยังอะแดปเตอร์ผ่านตัวสร้างและใช้ cursor.moveToPosition () ใน onBindViewHolder () เพื่อรับข้อมูลที่เกี่ยวข้อง
Gautam

2
คำแนะนำ: เรียก swapCursor () จากตัวสร้างดังนั้นคุณไม่ต้องทำซ้ำที่นั่น
Greg Ennis

2
@francas ภาพเคลื่อนไหวทำงานได้ดีถ้าคุณตั้งsetHasStableIds(true)
ฤๅ

2
@alders คุณได้ทำการเปลี่ยนแปลงอื่น ๆ นอกเหนือจาก setHasTableIds หรือไม่? ฉันได้ลองแล้วเช่นกัน ID ก็ตรงกันและทุกครั้งที่ฉันสลับเคอร์เซอร์รายการจะกลับไปด้านบน
dwbrito

89

วิธีแก้ปัญหาของฉันคือการถือสมาชิก CursorAdapter ไว้ในการใช้งาน RecyclerView.Adapter ของฉัน จากนั้นส่งการจัดการทั้งหมดในการสร้างมุมมองใหม่และผูกเข้ากับอะแดปเตอร์เคอร์เซอร์ดังนี้:

public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.ViewHolder> {

    // Because RecyclerView.Adapter in its current form doesn't natively 
    // support cursors, we wrap a CursorAdapter that will do all the job
    // for us.
    CursorAdapter mCursorAdapter;

    Context mContext;

    public MyRecyclerAdapter(Context context, Cursor c) {

        mContext = context;

        mCursorAdapter = new CursorAdapter(mContext, c, 0) {

            @Override
            public View newView(Context context, Cursor cursor, ViewGroup parent) {
                // Inflate the view here
            }

            @Override
            public void bindView(View view, Context context, Cursor cursor) {
                // Binding operations
            }
        };
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        View v1;

        public ViewHolder(View itemView) {
            super(itemView);
            v1 = itemView.findViewById(R.id.v1);
        }
    }

    @Override
    public int getItemCount() {
        return mCursorAdapter.getCount();
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // Passing the binding operation to cursor loader
        mCursorAdapter.getCursor().moveToPosition(position); //EDITED: added this line as suggested in the comments below, thanks :)
        mCursorAdapter.bindView(holder.itemView, mContext, mCursorAdapter.getCursor());

    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // Passing the inflater job to the cursor-adapter
        View v = mCursorAdapter.newView(mContext, mCursorAdapter.getCursor(), parent);
        return new ViewHolder(v);
    }
}

17
ขอบคุณสำหรับวิธีนี้มันใช้ได้กับฉันฉันแค่ต้องเพิ่มหนึ่งบรรทัดใน onBindViewHolder: 'mCursorAdapter.getCursor (). moveToPosition (ตำแหน่ง)' ก่อนที่จะเรียก bindView () บน mCursorAdapter
user1071762

8
@nbtk ทำไมไม่ถือ Cursor แทน CursorAdapter? ตามที่ฉันเข้าใจคุณไม่ได้ใช้ความสามารถใด ๆ ของ CursorAdapter
MyDogTom

14
@MyDogTom - ฉันไม่ถือเคอร์เซอร์เพราะคุณต้องล้างมันเมื่อคุณทำเสร็จและการตั้งค่าเป็น NULL นั้นไม่เพียงพอ CursorAdapter ดูแลเรื่องนี้อย่างสมบูรณ์แบบ
nbtk

3
@nbtk ฉันชอบความคิดนี้มากและฉันก็เล่นกับมันตั้งแต่นั้นมา ฉันลองเขียนบทคัดย่อเล็กน้อยและสร้างห้องสมุดซึ่งยังไม่ 100% ในที่ที่ฉันต้องการ แต่ก็ทำให้งานสำเร็จ คุณสนใจที่จะตรวจสอบหรือไม่? หากคุณมี github ฉันยินดีให้เครดิตคุณด้วยแรงบันดาลใจ github.com/androidessence/RecyclerViewCursorAdapter
AdamMc331

5
@nbtk ทำสิ่งต่อไปนี้เพื่อรีเฟรชมุมมองของคุณด้วยข้อมูลใหม่:public void changeCursor(Cursor cursor){ mCursorAdapter.changeCursor(cursor); notifyDataSetChanged(); }
Cijo

48

เนื่องจากคำถามของคุณระบุว่า "วิธีใช้RecyclerViewกับฐานข้อมูล" และคุณไม่ได้เจาะจงว่าคุณต้องการ SQLite หรืออย่างอื่นด้วยสิ่งRecyclerViewนี้ฉันจะให้โซลูชันที่เหมาะสมที่สุดแก่คุณ ฉันจะใช้Realmเป็นฐานข้อมูลและให้คุณแสดงข้อมูลทั้งหมดภายในRecyclerViewไฟล์. มีการสนับสนุนแบบสอบถามตรงกันเป็นอย่างดีโดยไม่ต้องใช้หรือLoadersAsyncTask

ทำไมต้องเป็นอาณาจักร? realm.io ประสิทธิภาพ Android

ขั้นตอนที่ 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อยู่ข้างในก็ตาม ฉันวางแผนที่จะเพิ่มรายการเพิ่มเติมในเลย์เอาต์นี้และด้วยเหตุนี้ทำให้มันยืดหยุ่นได้ในตอนนี้ :)

สำหรับคนที่อยากรู้อยากเห็นนี่คือลักษณะของรายการเดียวในปัจจุบัน เค้าโครงแถวรายการเดียวภายใน RecyclerView

ขั้นตอนที่ 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 ที่แสดงที่นี่ค่อนข้างล่าช้า แต่ภาพเคลื่อนไหวจะเกิดขึ้นเมื่อคุณเพิ่มรายการหรือลบออก

ข้อมูลจากฐานข้อมูลภายใน RecyclerView


ขอขอบคุณที่ให้ตัวอย่างที่ครอบคลุม ฉันมีคำถามสองสามข้อ ฉันต้องการจัดเก็บบิตแมปในขอบเขตเป็นไปได้หรือไม่? ถ้าใช่ประเภทคอลัมน์จะเป็นอย่างไรและจะจัดเก็บและเรียกคืนได้อย่างไร
Rakesh

1
@Rakesh ไม่เคยเก็บภาพไว้ในฐานข้อมูลใด ๆ เก็บไว้ในระบบไฟล์ของคุณและเก็บภาพในฐานข้อมูล
เสมอ

ขอบคุณสำหรับการตอบกลับอย่างรวดเร็ว ฉันกำลังสร้างแอพที่ฉันต้องการเก็บภาพสำหรับผู้ติดต่อของฉัน หากฉันจัดเก็บในระบบไฟล์อาจมีภาพหลายร้อยภาพ ฉันจะจัดการภาพเหล่านั้นบนระบบไฟล์ได้อย่างไร?
Rakesh

9
FWIW รวมถึงกราฟประสิทธิภาพของ Realm ในเม็ดมีดนั้นค่อนข้างไม่เกี่ยวข้องกับคำถามเกี่ยวกับการรีไซเคิล (ซึ่งจะเกี่ยวกับการดึงข้อมูลอย่างหลีกเลี่ยงไม่ได้)
JRaymond

ให้แถบเครื่องมือระบุว่าVivzนี่คือโซลูชัน @slidenerd หรือไม่
SymbolixAU

1

คุณสามารถใช้วิธีการที่จำเป็นทั้งหมดได้ด้วยตัวเอง ฉันเพิ่งทำการติดตั้งเองโดยเพียงแค่คัดลอกการวางโค้ดจาก CursorAdapter

public class MyAdapter extends RecyclerView.Adapter<ViewHolder> {

        protected boolean mDataValid;
        protected boolean mAutoRequery;
        protected Cursor mCursor;
        protected Context mContext;
        protected int mRowIDColumn;
        protected ChangeObserver mChangeObserver;
        protected DataSetObserver mDataSetObserver;
        protected FilterQueryProvider mFilterQueryProvider;
        public static final int FLAG_AUTO_REQUERY = 0x01;
        public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;

        public Cursor getCursor() {
            return mCursor;
        }

        //Recommended
        public MyAdapter(Context context, Cursor c, int flags) {
            init(context, c, flags);
        }

        public MyAdapter(Context context, Cursor c) {
            init(context, c, FLAG_AUTO_REQUERY);
        }

        public MyAdapter(Context context, Cursor c, boolean autoRequery) {
            init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
        }

        void init(Context context, Cursor c, int flags) {
            if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
                flags |= FLAG_REGISTER_CONTENT_OBSERVER;
                mAutoRequery = true;
            } else {
                mAutoRequery = false;
            }
            boolean cursorPresent = c != null;
            mCursor = c;
            mDataValid = cursorPresent;
            mContext = context;
            mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
            if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
                mChangeObserver = new ChangeObserver();
                mDataSetObserver = new MyDataSetObserver();
            } else {
                mChangeObserver = null;
                mDataSetObserver = null;
            }

            if (cursorPresent) {
                if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
                if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
            }
        }

        // Create new views (invoked by the layout manager)
        @Override
        public ViewHolder onCreateViewHolder(final ViewGroup parent,
                                             int viewType) {
            // create a new view
            final View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.list_item, parent, false);
            // set the view's size, margins, paddings and layout parameters

            ViewHolder vh = new ViewHolder(view, mCursor, new ViewHolder.IMyViewHolderClicks() {

                @SuppressLint("NewApi")
                @Override
                public void onClick(Cursor cursor) {
                    Log.e("Item :", cursor.getString(cursor.getColumnIndex(MyDatabaseHelper.MW_NAAM)));
                    Intent intent = new Intent(TasksListFragment.this.getActivity(), DetailActivity.class);
                    intent.putExtra(DetailActivity.EXTRA_PARAM_ID, cursor.getLong(cursor.getColumnIndex(MyDatabaseHelper.MW_ID)));

                    ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
                            TasksListFragment.this.getActivity(),

                            // Now we provide a list of Pair items which contain the view we can transitioning
                            // from, and the name of the view it is transitioning to, in the launched activity
                            new Pair<View, String>(
                                    view.findViewById(R.id.imageview_item),
                                    DetailActivity.VIEW_NAME_HEADER_IMAGE),
                            new Pair<View, String>(
                                    view.findViewById(R.id.textview_name),
                                    DetailActivity.VIEW_NAME_HEADER_TITLE)
                    );

                    // Now we can start the Activity, providing the activity options as a bundle
                    startActivity(intent, activityOptions.toBundle());
                    // END_INCLUDE(start_activity)
                }
            });
            return vh;
        }

        // Replace the contents of a view (invoked by the layout manager)
        @SuppressLint("NewApi")
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            // - get element from your dataset at this position
            // - replace the contents of the view with that element
            final Cursor cursor = getItem(position);

            holder.mTextView.setText(cursor.getString(cursor.getColumnIndex(MyDatabaseHelper.MW_NAAM)));
            holder.mImageView.setTransitionName("grid:image:" + cursor.getLong(cursor.getColumnIndex(MyDatabaseHelper.MW_ID)));
            holder.mTextView.setTransitionName("grid:name:" + cursor.getLong(cursor.getColumnIndex(MyDatabaseHelper.MW_ID)));
        }

        //@Override
        // public View getView(int position, View view, ViewGroup viewGroup) {
        //     return view;
        // }

        // Return the size of your dataset (invoked by the layout manager)
        @Override
        public int getItemCount() {
            return getCount();
        }

        public int getCount() {
            if (mDataValid && mCursor != null) {
                return mCursor.getCount();
            } else {
                return 0;
            }
        }

        public Cursor getItem(int position) {
            if (mDataValid && mCursor != null) {
                mCursor.moveToPosition(position);
                return mCursor;
            } else {
                return null;
            }
        }

        @Override
        public long getItemId(int position) {
            if (mDataValid && mCursor != null) {
                if (mCursor.moveToPosition(position)) {
                    return mCursor.getLong(mRowIDColumn);
                } else {
                    return 0;
                }
            } else {
                return 0;
            }
        }

        public Cursor swapCursor(Cursor newCursor) {
            if (newCursor == mCursor) {
                return null;
            }
            Cursor oldCursor = mCursor;
            if (oldCursor != null) {
                if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
                if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
            }
            mCursor = newCursor;
            if (newCursor != null) {
                if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
                if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
                mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
                mDataValid = true;
                // notify the observers about the new cursor
                notifyDataSetChanged();
            } else {
                mRowIDColumn = -1;
                mDataValid = false;
                // notify the observers about the lack of a data set
                notifyDataSetInvalidated();
            }
            return oldCursor;
        }

        public void changeCursor(Cursor cursor) {
            Cursor old = swapCursor(cursor);
            if (old != null) {
                old.close();
            }
        }

        public CharSequence convertToString(Cursor cursor) {
            return cursor == null ? "" : cursor.toString();
        }

        public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
            if (mFilterQueryProvider != null) {
                return mFilterQueryProvider.runQuery(constraint);
            }
            return mCursor;
        }


        public FilterQueryProvider getFilterQueryProvider() {
            return mFilterQueryProvider;
        }

        public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
            mFilterQueryProvider = filterQueryProvider;
        }

        protected void onContentChanged() {
            if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
                if (false) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
                mDataValid = mCursor.requery();
            }
        }

        private class ChangeObserver extends ContentObserver {
            public ChangeObserver() {
                super(new Handler());
            }

            @Override
            public boolean deliverSelfNotifications() {
                return true;
            }

            @Override
            public void onChange(boolean selfChange) {
                onContentChanged();
            }
        }

        private class MyDataSetObserver extends DataSetObserver {
            @Override
            public void onChanged() {
                mDataValid = true;
                notifyDataSetChanged();
            }

            @Override
            public void onInvalidated() {
                mDataValid = false;
                notifyDataSetInvalidated();
            }
        }


        private final DataSetObservable mDataSetObservable = new DataSetObservable();

        public void registerDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.registerObserver(observer);
        }

        public void unregisterDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.unregisterObserver(observer);
        }

        public void notifyDataSetInvalidated() {
            mDataSetObservable.notifyInvalidated();
        }
    }

แล้ว Loader ในคลาสนี้อยู่ที่ไหน?
IgorGanapolsky

ตัวโหลดจะใช้อะแดปเตอร์นี้ โปรดดูdeveloper.android.com/guide/components/loaders.htmlสำหรับตัวอย่างวิธีใช้ตัวโหลด ในตัวอย่างนี้ SimpleCursorAdapter จะเป็น MyAdapter
Arno

คุณหมายถึงอะไร SimpleCursorAdapter? คุณกำลังขยาย RecyclerView.Adapter
IgorGanapolsky

1
ฉันหมายความว่าถ้าคุณเห็น SimpleCursorAdapter ในตัวอย่างคุณสามารถใส่ MyAdapter แทนสิ่งนั้นได้ ไม่สำคัญว่าคุณจะขยายอะแดปเตอร์ใดตราบเท่าที่มีวิธีการสลับและวิธีการอื่น ๆ
Arno

ดูเหมือนการเข้ารหัสเล็กน้อยถ้าเทียบกับคำตอบของ @nbtk ข้างต้น
eRaisedToX


1

คำตอบอื่นเนื่องจากฉันไม่ชอบคำตอบที่ยอมรับ (ซึ่ง imo ไม่มีการใช้งานที่เข้าใจง่าย)

ต่อไปนี้เป็นการใช้งานของฉันเองซึ่งคล้ายกับ (และได้รับแรงบันดาลใจบางส่วนจาก) SimpleCursorAdapter:

public class RecyclerViewSimpleCursorAdapter extends RecyclerView.Adapter {
    private int mLayout;
    private Cursor mCursor;
    private String[] mFrom;
    private int[] mTo;

    private boolean mAutoRequery;
    private ContentObserver mContentObserver;

    /**
     * Standard constructor.
     *
     * @param layout resource identifier of a layout file that defines the views for this list item. The layout file should include at least those named views defined in "to"
     * @param c      The database cursor. Can be null if the cursor is not available yet.
     * @param from   A list of column names representing the data to bind to the UI. Can be null if the cursor is not available yet.
     * @param to     The views that should display column in the "from" parameter. These should all be TextViews and ImageViews. The first N views in this list are given the values of the first N columns in the from parameter. Can be null if the cursor is not available yet.
     */
    public RecyclerViewSimpleCursorAdapter(int layout, Cursor c, String[] from, int[] to, boolean autoRequery) {
        mLayout = layout;
        mCursor = c;
        mFrom = from;
        mTo = to;
        mAutoRequery = autoRequery;

        if (mAutoRequery) {
            initializeContentObserver();
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new RecyclerView.ViewHolder(
                LayoutInflater.from(parent.getContext())
                        .inflate(mLayout, parent, false)
        ) {
        };
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        mCursor.moveToPosition(position);

        if (mFrom == null || mTo == null)
            return;

        for (int i = 0; i < mFrom.length && i < mTo.length; i++) {
            String from = mFrom[i];
            int columnIndex = mCursor.getColumnIndex(from);
            String value = mCursor.getString(columnIndex);
            View view = holder.itemView.findViewById(mTo[i]);

            if (view instanceof TextView) {
                ((TextView) view).setText(value);
            } else if (view instanceof ImageView) {
                try {
                    ((ImageView) view).setImageResource(Integer.parseInt(value));
                } catch (NumberFormatException nfe) {
                    ((ImageView) view).setImageURI(Uri.parse(value));
                }
            } else {
                throw new IllegalStateException(view.getClass().getName() + " is not a view that can be bound by this RecyclerViewSimpleCursorAdapter");
            }
        }
    }

    @Override
    public int getItemCount() {
        return mCursor  != null ? mCursor.getCount() : 0;
    }

    private void initializeContentObserver() {
        mContentObserver = new ContentObserver(new Handler()) {
            @Override
            public boolean deliverSelfNotifications() {
                return true;
            }

            @Override
            public void onChange(boolean selfChange) {
                notifyDataSetChanged();
            }
        };
        mCursor.registerContentObserver(mContentObserver);
    }

    /**
     * Change the underlying cursor to a new cursor. If there is an existing cursor it will be closed.
     *
     * @param cursor The new cursor to be used
     */
    public void changeCursor(Cursor cursor) {
        Cursor oldCursor = mCursor;
        if (mAutoRequery) {
            if (mCursor != null) {
                mCursor.unregisterContentObserver(mContentObserver);
            }

            mContentObserver = new ContentObserver(new Handler()) {
                @Override
                public boolean deliverSelfNotifications() {
                    return true;
                }

                @Override
                public void onChange(boolean selfChange) {
                    notifyDataSetChanged();
                }
            };

            mCursor = cursor;
            if (mCursor != null) {
                mCursor.registerContentObserver(mContentObserver);
            }
        }

        notifyDataSetChanged();

        if (oldCursor != null && oldCursor != mCursor) {
            oldCursor.close();
        }
    }

    /**
     * Change the cursor and change the column-to-view mappings at the same time.
     *
     * @param cursor The database cursor. Can be null if the cursor is not available yet.
     * @param from A list of column names representing the data to bind to the UI. Can be null if the cursor is not available yet.
     * @param to The views that should display column in the "from" parameter. These should all be TextViews or ImageViews. The first N views in this list are given the values of the first N columns in the from parameter. Can be null if the cursor is not available yet.
     */
    public void changeCursorAndColumns(Cursor cursor, String[] from, int[] to) {
        mFrom = from;
        mTo = to;
        changeCursor(cursor);
    }

    /**
     * Returns the cursor.
     * @return the cursor
     */
    public Cursor getCursor() {
        return mCursor;
    }
}

คุณสามารถแก้ไขได้สำหรับการใช้งานเฉพาะอื่น ๆ แต่การมีเคอร์เซอร์จะใช้งานได้เช่นSimpleCursorAdapterเดียวกับไฟล์RecyclerView.


0

หลังจากดึงข้อมูลจาก DB และจัดเก็บในรายการอาจเป็นดังนี้:

    dbHelper = new BooksDbAdapter(this);
    dbHelper.open();
    //Clean all data
    dbHelper.deleteAllZist();
    //Add some data
    dbHelper.insertSomeRecords();
    List<String> mylist = dbHelper.getArrayColumn(3);

เพิ่มข้อมูลในการดูรีไซเคิล

list = new ArrayList<DataObject>();
 Integer i=0;
    for (String lst : mylist) {
        list.add(i++, new DataObject(lst,
                "The RecyclerView widget is a more advanced and flexible 
               version of ListView."));
    }

    recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    recyclerView.setHasFixedSize(true);
    myRecAdapter = new RecyclerviewAdapter(list, Zist1ChapterActivity.this);

0

ด้านล่างนี้คือการใช้ cursoradapter ของฉันสำหรับมุมมองรีไซเคิล รองรับ OnItemClickListener, OnLongItemClickListener, OnfooterClickListener, Sections และส่วนท้าย ไม่รองรับ Header, onHeaderClickListner, fast scroller, sticky header หรือ sticky section

เพียงแค่ขยายอะแดปเตอร์นี้และสร้างอะแดปเตอร์ของคุณเอง แทนที่วิธีการที่มีให้ และส่งเคอร์เซอร์จากเมธอด OnCursorLoadFinished หากสร้างอะแด็ปเตอร์แล้วให้ swapCursor ()

package com.tracker.paisa;

import android.database.Cursor;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;


/**
 * @author Rahul Upadhyay (https://github.com/devDroidRaul)
 * Supports footer
 * Onitemclicklistener, OnItemLongClickListener, OnFooterClickListener
 * Supports Sections.
 *
 * Does Not support,Header, OnHeaderClickListener, FastScroller, StickySection (this can b done with item decor)
 * Pull requests are welcome for improvements.
 *
 * Override this to give logic to place subheaders between items.
 * public abstract boolean onPlaceSubheaderBetweenItems(int position);
 *
 * create seperate viewHolders for item, subheaders and footer. and return required views.
 *
 * @Override below methods for individual item type.
 * public abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);
 * public abstract SH onCreateSubheaderViewHolder(ViewGroup parent, int viewType);
 * public abstract FH onCreateFooterViewHolder(ViewGroup parent, int viewType);
 *
 * Bind your views with data here.
 * @Override below methods to bind data to individual item types.
 * public abstract void onBindSubHeaderViewHolder(SH holder, Cursor cursor);
 * public abstract void onBindItemViewHolder(VH holder, Cursor cursor);
 * public abstract void onBindFooterViewHolder(FH holder, int itemPosition);
 *
 * Item type -1,-2,-3 are reserved, kindly do not pass them in getItemViewType.
 */

public abstract class RecyclerViewCursorAdapter<SH extends RecyclerView.ViewHolder, VH extends RecyclerView.ViewHolder, FH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public static final String TAG = RecyclerViewCursorAdapter.class.getSimpleName();

    private static final int TYPE_SECTION_HEADER = -1;

    private static final int TYPE_MAIN_HEADER = -2;
    private static final int TYPE_FOOTER = -3;

   // private int headerLayout=0,viewLayout=0;
    boolean createHeader;

    private List<Integer> subheaderPositions = new ArrayList<>();

    private Cursor mCursor;
    private boolean mDataValid,footerRequired=false;
    private int mRowIDColumn;
    private SparseBooleanArray mSelectedItemsIds;



   // public RecyclerViewCursorAdapter() { }

    //constructor
    public RecyclerViewCursorAdapter(Cursor c,boolean footerRequired) {
        setHasStableIds(true);
        swapCursor(c);
        this.footerRequired = footerRequired;

        this.mSelectedItemsIds = new SparseBooleanArray();
    }



    // interface for listning click on recycler view;
    public interface OnItemClickedListener{
        void OnItemClick(int id, Object data);

        void onItemLongClick(int id);
    }

    OnItemClickedListener onItemClickedListener;

    public void setOnItemClickedListener(OnItemClickedListener onItemClickedListener) {
        this.onItemClickedListener = onItemClickedListener;
    }

    public interface OnFooterClickedListener{
        void onFooterClick(Object data);
    }

    OnFooterClickedListener onFooterClickedListener;
    public void setOnFooterClickedListener( OnFooterClickedListener onFooterClickedListener){
        this.onFooterClickedListener = onFooterClickedListener;
    }

    public interface OnHeaderClickedListener{
        void onHeaderClick(Object data);
    }

    OnHeaderClickedListener onHeaderClickedListener;
    public void setOnHeaderClickedListener( OnHeaderClickedListener onHeaderClickedListener){
        this.onHeaderClickedListener = onHeaderClickedListener;
    }

    private void initSubheaderPositions() {
        subheaderPositions.clear();

        if(getItemSize() != 0) {
            //TODO:Handle This please.
            //subheaderPositions.add(0);
        } else {
            return;
        }

        for(int i = 1; i < getItemSize(); i++) {
            if(onPlaceSubheaderBetweenItems(i - 1)) {
                subheaderPositions.add(i + subheaderPositions.size());
            }
        }
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {

            initSubheaderPositions();



    }

    /**
     * Called when adapter needs to know whether to place subheader between two neighboring
     * items.
     *
     * @return true if you want to place subheader between two neighboring
     * items.
     */
    public abstract boolean onPlaceSubheaderBetweenItems(int position);

    public abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);

    public abstract SH onCreateSubheaderViewHolder(ViewGroup parent, int viewType);

    public abstract FH onCreateFooterViewHolder(ViewGroup parent, int viewType);

    public abstract void onBindSubHeaderViewHolder(SH holder, Cursor cursor);

    public abstract void onBindItemViewHolder(VH holder, Cursor cursor);

    public abstract void onBindFooterViewHolder(FH holder, int itemPosition);






    public abstract int getItemSize();

    /**
     * Return the view type of the item at position for the purposes
     * of view recycling.
     * Don't return -1. It's reserved for subheader view type.
     */
    public int getViewType(int position) {
        return 0;
    }

    @Override
    public final int getItemViewType(int position) {

        if(isSubheaderOnPosition(position)) {
            return TYPE_SECTION_HEADER;
        } if(footerRequired && getCursor().getPosition()==(getCursor().getCount()-1)){
            return TYPE_FOOTER;
        }else {
            return getViewType(position);
        }
    }

    public boolean isFooterAdded(){
        return footerRequired;
    }
    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.d("RVCA-OCVH","create viewholder");
        if(viewType == TYPE_SECTION_HEADER) {
            return onCreateSubheaderViewHolder(parent, viewType);
        } if(footerRequired&&viewType == TYPE_FOOTER){
            return onCreateFooterViewHolder(parent, viewType);
        }else {
            return onCreateItemViewHolder(parent, viewType);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        Log.d("RVCA-OBVH","bind viewholder");
        Log.d("RVCA-OBVH","subheader position:"+isSubheaderOnPosition(position));
        if(isSubheaderOnPosition(position)) {
            if (!mDataValid) {
                throw new IllegalStateException("Cannot bind viewholder when cursor is in invalid state.");
            }
            if (!mCursor.moveToPosition(getItemPositionForViewHolder(position))) {
                throw new IllegalStateException("Could not move cursor to position " + getItemPositionForViewHolder(position) + " when trying to bind viewholder");
            }

            onBindSubHeaderViewHolder((SH)holder, mCursor);
        }if(footerRequired && position==getItemCount()-1){
            Log.d("RVCA-OBVH","bind footerHolder");
            onBindFooterViewHolder((FH) holder,position);

        } else {
            if (!mDataValid) {
                throw new IllegalStateException("Cannot bind viewholder when cursor is in invalid state.");
            }
            if (!mCursor.moveToPosition(getItemPositionForViewHolder(position))) {
                throw new IllegalStateException("Could not move cursor to position " + getItemPositionForViewHolder(position) + " when trying to bind viewholder");
            }
            // if(!mCursor.isAfterLast()) {
            //   mCursor.moveToPosition(position);
            onBindItemViewHolder((VH)holder, mCursor);

        }
    }

    @Override
    public final int getItemCount() {
        return getItemSize() + subheaderPositions.size()+(footerRequired?1:0);
    }

    public void notifyDataChanged() {
        initSubheaderPositions();
        notifyDataSetChanged();
    }

    public void notifyItemInsertedAtPosition(int itemPosition) {
        if (itemPosition == 0) {
            if (getItemCount() == 1 || onPlaceSubheaderBetweenItems(itemPosition)) {
                subheaderPositions.add(0, 0);
                increaseSubheaderPositions(1, 2);
                notifyItemRangeInserted(0, 2);
            } else {
                increaseSubheaderPositions(1, 1);
                notifyItemInserted(1);
            }
        } else if (itemPosition == getItemSize() - 1) {
            if (onPlaceSubheaderBetweenItems(itemPosition - 1)) {
                subheaderPositions.add(getItemCount() - 1);
                notifyItemRangeInserted(getItemCount() - 1, 2);
            } else {
                notifyItemInserted(getItemPositionInRecyclerView(itemPosition));
            }
        } else {
            if (onPlaceSubheaderBetweenItems(itemPosition - 1) && onPlaceSubheaderBetweenItems(itemPosition)) {
                final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition - 1);
                final int countOfSubheadersBeforePosition = getCountOfSubheadersBeforePosition(itemPositionInRv);
                subheaderPositions.add(countOfSubheadersBeforePosition, itemPositionInRv + 1);
                increaseSubheaderPositions(countOfSubheadersBeforePosition + 1, 2);
                notifyItemRangeInserted(itemPositionInRv + 1, 2);
            } else if (onPlaceSubheaderBetweenItems(itemPosition)){
                final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition - 1);
                increaseSubheaderPositions(getCountOfSubheadersBeforePosition(itemPositionInRv), 1);
                notifyItemInserted(itemPositionInRv + 1);
            } else if (onPlaceSubheaderBetweenItems(itemPosition - 1)) {
                final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition);
                increaseSubheaderPositions(getCountOfSubheadersBeforePosition(itemPositionInRv), 1);
                notifyItemInserted(itemPositionInRv);
            } else {
                final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition);
                increaseSubheaderPositions(getCountOfSubheadersBeforePosition(itemPositionInRv), 1);
                notifyItemInserted(itemPositionInRv);
            }
        }
    }

    public void notifyItemChangedAtPosition(int itemPosition) {
        final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition);
        notifyItemChanged(itemPositionInRv);
    }

    public void notifyItemRemovedAtPosition(int itemPosition) {

        final int itemPositionInRv = getItemPositionInRecyclerView(itemPosition);

        for (int i = 1; i < subheaderPositions.size(); i++) {
            final int subheaderPosition = subheaderPositions.get(i);
            if (subheaderPosition > itemPositionInRv) {
                final int previousSubheaderPosition = subheaderPositions.get(i - 1);
                if (subheaderPosition - previousSubheaderPosition == 2) {
                    subheaderPositions.remove(subheaderPositions.indexOf(previousSubheaderPosition));
                    decreaseSubheaderPositions(subheaderPositions.indexOf(subheaderPosition), 2);
                    notifyItemRangeRemoved(itemPositionInRv - 1, 2);
                } else {
                    decreaseSubheaderPositions(subheaderPositions.indexOf(subheaderPosition), 1);
                    notifyItemRemoved(itemPositionInRv);
                }
                return;
            }
        }

        final int lastSubheaderPosition = subheaderPositions.get(subheaderPositions.size() - 1);
        if (itemPositionInRv - lastSubheaderPosition == 1 && getItemCount() == itemPosition + subheaderPositions.size()) {
            subheaderPositions.remove(subheaderPositions.size() - 1);
            notifyItemRangeRemoved(itemPositionInRv - 1, 2);
        } else {
            notifyItemRemoved(itemPositionInRv);
        }
    }

    public void setGridLayoutManager(final GridLayoutManager gridLayoutManager) {
        gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                if(subheaderPositions.contains(position)) {
                    return gridLayoutManager.getSpanCount();
                } else {
                    return 1;
                }
            }
        });
    }

    public boolean isSubheaderOnPosition(int position) {
        return subheaderPositions.contains(position);
    }

    public int getCountOfSubheadersBeforePosition(int position) {
        int count = 0;
        for(int subheaderPosition : subheaderPositions) {
            if(subheaderPosition < position) {
                count++;
            }
        }
        return count;
    }

    public int getItemPositionInRecyclerView(int position) {
        int countOfItems = 0;
        for (int i = 1; i < subheaderPositions.size(); i++) {
            final int previousSubheaderPosition = subheaderPositions.get(i - 1);
            final int nextSubheaderPosition = subheaderPositions.get(i);
            countOfItems += nextSubheaderPosition - previousSubheaderPosition - 1;
            if (countOfItems > position) {
                return position + i;
            }
        }
        return position + subheaderPositions.size();
    }

    public int getItemPositionForViewHolder(int viewHolderPosition) {
        return viewHolderPosition - getCountOfSubheadersBeforePosition(viewHolderPosition);
    }

    private void decreaseSubheaderPositions(int startSubheaderPosition, int decreaseNum) {
        for (int i = startSubheaderPosition; i < subheaderPositions.size(); i++) {
            final int subheaderPosition = subheaderPositions.get(i);
            subheaderPositions.set(i, subheaderPosition - decreaseNum);
        }
    }

    private void increaseSubheaderPositions(int startSubheaderPosition, int increaseNum) {
        for (int i = startSubheaderPosition; i < subheaderPositions.size(); i++) {
            final int subheaderPosition = subheaderPositions.get(i);
            subheaderPositions.set(i, subheaderPosition + increaseNum);
        }
    }

    private List<Integer> getSubheaderPositions() {
        return subheaderPositions;
    }

    public int getSubheaderCount() {
        return subheaderPositions.size();
    }

    public void swapCursor(Cursor newCursor) {
        Log.d("RVCA-SC","swap cursor");
        if (newCursor == mCursor) {
            Log.d("RVCA-SC","same cursor doing nothing");
            return;
        }

        if (newCursor != null) {
            Log.d("RVCA-SC","swap cursor not null");
            mCursor = newCursor;
            mRowIDColumn = mCursor.getColumnIndexOrThrow("_id");
            mDataValid = true;

            // notify the observers about the new cursor
            notifyDataChanged();
        } else {
            Log.d("RVCA-SC","swap cursor null");
            notifyItemRangeRemoved(0, getItemCount());
            mCursor = null;
            mRowIDColumn = -1;
            mDataValid = false;
        }
    }

    public Cursor getCursor(){
        return mCursor ;
    }

    @Override
    public long getItemId(int position) {
        if (isSubheaderOnPosition(position))
            return position;
        else {
            int cursorPosition = getItemPositionForViewHolder(position);
            Cursor cursor = getCursor();
            if (hasOpenCursor() && cursor.moveToPosition(cursorPosition)) {
                return cursor.getLong(cursor.getColumnIndex("_id"));
            }
            return NO_CURSOR_POSITION;
        }
    }
    public static final int NO_CURSOR_POSITION = -99;

    protected boolean hasOpenCursor() {
        Cursor cursor = getCursor();
        if (cursor == null || cursor.isClosed()) {
            swapCursor(null);
            return false;
        }
        return true;
    }

    //Methods to do Selection

    public void toggleSelection(int position) {
        selectView(position, !mSelectedItemsIds.get(position));
    }


    //Remove selected selections
    public void removeSelection() {
        mSelectedItemsIds = new SparseBooleanArray();
        notifyDataSetChanged();
    }


    //Put or delete selected position into SparseBooleanArray
    public void selectView(int position, boolean value) {
        if (value)
            mSelectedItemsIds.put(position, value);
        else
            mSelectedItemsIds.delete(position);

        // notifyItemChangedAtPosition(position);
        notifyDataSetChanged();
    }

    //Get total selected count
    public int getSelectedCount() {
        return mSelectedItemsIds.size();
    }

    //Return all selected ids
    public SparseBooleanArray getSelectedIds() {
        return mSelectedItemsIds;
    }

    public void setSelectedItemIds(SparseBooleanArray selectedItemIds){
        this.mSelectedItemsIds=selectedItemIds;
    }


}

0

การใช้งานที่ง่ายที่สุดคือการใช้วัตถุเคอร์เซอร์ในอะแด็ปเตอร์และส่งเคอร์เซอร์ไปยังตัวสร้างผู้ดูสำหรับการแก้ไขมุมมองและเปลี่ยนตำแหน่งเคอร์เซอร์ใน onBindViewholder

หากคุณใช้ cursor Loader ให้เรียกใช้เมธอด setCursor onLoadfinished()และส่งค่า null ในonLoadReset()

public class PetCursorRecyclerViewAdapter extends RecyclerView.Adapter<PetCursorRecyclerViewAdapter.ViewHolder> {
Cursor cursor = null;
Context context;

public PetCursorRecyclerViewAdapter(Context context) {
    this.context = context;

}

public void setCursor(Cursor cursor) {
    this.cursor = cursor;
    notifyDataSetChanged();
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(context).inflate(R.layout.catalog_item, parent, false);
    return new ViewHolder(v);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    this.cursor.moveToPosition(position);
    holder.bindModel(this.cursor);
}

/*   getItemCount() returns the count of videos from the Cursor, or 0 if the
   Cursor is null (mimicking the behavior of CursorAdapter, which also treats
   a null Cursor as merely being one that has no rows)*/
@Override
public int getItemCount() {
    if (cursor == null) {
        return 0;
    } else {
        return cursor.getCount();
    }
}

public static class ViewHolder extends RecyclerView.ViewHolder {
    private TextView name_tv, breed_tv;

    public ViewHolder(View itemView) {
        super(itemView);
        name_tv = (TextView) itemView.findViewById(R.id.name_tv);
        breed_tv = (TextView) itemView.findViewById(R.id.breed_tv);
    }

    public void bindModel(Cursor cursor) {
        int name_index = cursor.getColumnIndexOrThrow(PetsContract.PetEntry.COLUMN_PET_NAME);
        int breed_index = cursor.getColumnIndexOrThrow(PetsContract.PetEntry.COLUMN_PET_BREED);
        name_tv.setText(cursor.getString(name_index));
        String breed = cursor.getString(breed_index);
        if (TextUtils.isEmpty(breed)) {
            breed = "Unknown Breed";
        }
        breed_tv.setText(breed);
    }
}    

}


0

ในที่สุดเราก็มีการใช้งาน RecyclerView.Adapter สำหรับฐานข้อมูล / เครือข่าย

รับรู้ด้วยส่วนประกอบสถาปัตยกรรม Android :

ฐานข้อมูลห้อง:ชั้นฐานข้อมูลที่ด้านบนของฐานข้อมูล SQLite ที่ดูแลงานทางโลกที่คุณเคยจัดการกับSQLiteOpenHelperไฟล์. ตัวยึดฐานข้อมูลที่ทำหน้าที่เป็นจุดเชื่อมต่อไปยังฐานข้อมูล SQLite พื้นฐาน ฐานข้อมูล Room ใช้ DAO เพื่อออกแบบสอบถามไปยังฐานข้อมูล SQLite

ViewModel:ให้ข้อมูลกับ UI ทำหน้าที่เป็นศูนย์กลางการสื่อสารระหว่าง Repository และ UI ซ่อนที่มาของข้อมูลจาก UI อินสแตนซ์ ViewModel อยู่รอดจากกิจกรรม / ส่วนย่อย

LiveData:คลาสผู้ถือข้อมูลที่สามารถสังเกตได้ เก็บ / แคชข้อมูลเวอร์ชันล่าสุดเสมอ แจ้งผู้สังเกตการณ์เมื่อข้อมูลมีการเปลี่ยนแปลง LiveData ตระหนักถึงวงจรชีวิต ส่วนประกอบ UI เพียงแค่สังเกตข้อมูลที่เกี่ยวข้องและอย่าหยุดหรือทำการสังเกตต่อ LiveData จะจัดการทั้งหมดนี้โดยอัตโนมัติเนื่องจากทราบถึงการเปลี่ยนแปลงสถานะของวงจรชีวิตที่เกี่ยวข้องในขณะที่สังเกต


-2

คุณสามารถใช้ฐานข้อมูล SQLite เพื่อจัดเก็บรายละเอียด เข้าถึงข้อมูลได้ง่าย คุณสามารถตรวจสอบรหัสของฉันบน github https://github.com/thiru-wta/ToDo

     database = new Database(this);
    getSupportActionBar().setTitle("To Do List");
    etAddItems = (EditText) findViewById(R.id.etAddItem);
    btnAdd = (Button) findViewById(R.id.btnAdd);
    mRecyclerView = (RecyclerView) findViewById(R.id.listView);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    adapter = new RecyclerAdapter(this, listData);
    mRecyclerView.setAdapter(adapter);

    btnAdd.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            event_name = etAddItems.getText().toString();
            String getname="";

              database.storeEventDetails(event_name);

                 getname = database.getEventDetails(event_name);


            if (getname.length() != 0) {
                listData.add(getname);
                etAddItems.setText("");

                adapter.notifyData(listData);
            }

วิธีการฐานข้อมูล:

    public void storeEventDetails(String event_name, long timerStart) {
    SQLiteDatabase db1 = getWritableDatabase();
    db1.execSQL("insert into '"+event_details_tbl+"' values('" + event_name + "')");
    db1.close();
}

รับวิธีการ:

     public String getEventDetails(String event_name) {
    SQLiteDatabase db1 = getReadableDatabase();
    Cursor cur = db1.rawQuery("select * from '"+event_details_tbl+"' where     event_name ='" + event_name + "'", null);
    cur.moveToFirst();
    String evName = null;
    if (cur != null) {
        do {
            int eventName = cur.getColumnIndex("event_name");

            String ev_name = cur.getString(eventName);

            evName = ev_name;
        } while (cur.moveToNext());

    }
    cur.close();
    db1.close();
    return evName;

}     

-4

คุณต้องทำตามขั้นตอนเหล่านี้:

  • ในเครื่องรีไซเคิลview.adapterสร้าง ArrayList หรือ Iterator ที่เริ่มต้นด้วยค่า null
  • สร้างวิธีการเช่น swapcursor swapdata(Arraylist<T> data)อดีตเช่น ภายในคุณให้ค่าใหม่กับรายการอาร์เรย์ตัววนซ้ำหรือโครงสร้างใด ๆ ที่ระบบสามารถวนซ้ำได้โดยใช้ตำแหน่งจำนวนเต็ม int the bindview ค่าของวิธีนี้จะถูกส่งผ่านในไฟล์onloaderfinished(). แล้วโทรในเวลาเพียงหลังจาก assignement notifydatachange(); สิ่งเหล่านี้จะขอRecyclerViewให้วาดใหม่รายการทั้งหมดด้วยข้อมูลใหม่ของ ArrayList

ในกิจกรรมหรือแฟรกเมนต์ที่คุณใช้ loadercalback ให้สร้างเมธอดที่แปลงเคอร์เซอร์เป็นรายการอาร์เรย์หรือตัววนซ้ำขึ้นอยู่กับโครงสร้างข้อมูลที่เลือกในอะแด็ปเตอร์

ด้วยวิธีนี้คุณสามารถใช้ loadercalback ได้ตลอดเวลาโดยไม่ปิดกั้นเธรดหลัก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.