使用带有数据库的回收视图

目前还没有可用的侯选实现。

可能随着官方发布,谷歌会添加它。

由于目前 RecyclerView还不支持 CursorAdapter,那么我们如何在数据库中使用 RecyclerView呢?有什么建议吗?

93851 次浏览

如果使用 CursorLoader运行查询,并且希望使用 RecyclerView而不是 ListView

你可以试试我的 CursorRecyclerViewAdapter: 回收视图中的 CursorAdapter

您可以自己实现所有必需的方法。我最近通过从 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();
}
}

你只需遵循以下步骤:

  • 在回收器 view.adapter中,创建一个用空值初始化的 ArrayList 或 Iterator。
  • 创建一个类似 ex swapcursor 的方法,例如 swapdata(Arraylist<T> data)。在内部,您将为数组列表、迭代器或任何系统可以使用 bindview 中的整数位置进行迭代的结构提供一个新值。此方法的值在 onloaderfinished()中传递。然后在赋值 notifydatachange()之后调用; 它们会要求 RecyclerView用 ArrayList 的新数据重新绘制所有列表。

在使用 loadercalback 的活动或片段中,创建一个方法,根据适配器中选择的数据结构,将游标转换为数组列表或迭代器。

这样,您总是可以在不阻塞主线程的情况下使用 loadercalback。

我的解决方案是在我的回收视图中保持 CursorAdapter 成员。适配器实现。然后传递创建新视图的所有处理并将其绑定到游标适配器,如下所示:

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);
}
}

既然您的问题是“如何在数据库中使用 RecyclerView”,而且您没有具体说明是否需要使用 SQLite 或其他 RecyclerView,那么我将给出一个非常优化的解决方案。我将使用 王国作为数据库,并让您显示您的 RecyclerView内的所有数据。它也支持异步查询,而不需要使用 LoadersAsyncTask

为什么是王国? realm.io performance Android

第一步

添加王国的等级依赖项,最新版本的依赖项找到 给你

第二步

例如,创建模型类,让我们说一些简单的东西,比如 Data,它有两个字段,一个字符串显示在 RecyclerView行中,一个时间戳用作 itemId,允许 RecyclerView为项目制作动画。注意,我在下面扩展了 RealmObject,因为它将 Data类存储为表,所有属性将存储为该表 Data的列。在我的示例中,我已经将数据文本标记为主键,因为我不希望多次添加字符串。但是如果您喜欢有副本,那么将时间戳设置为@PrimaryKey。您可以拥有一个没有主键的表,但是如果您在创建该表之后尝试更新该行,那么它将会导致问题。在编写这个答案时,组合主键不受域的支持。

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;
}
}

第三步

创建 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作为 root 用户,尽管其中有一个 TextView。我计划在这个布局中添加更多的项目,因此现在使它更加灵活:)

对于那些好奇的人来说,这就是当前一件物品的样子。 single item row layout inside RecyclerView

第四步

创建 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的位置发生了删除,但我没有调用 notifyItemInsertednotifyItemRangeChanged,因为没有直接的方法知道哪个位置的项目是插入到数据库,因为王国项目没有存储在一个有序的方式。每当从数据库中添加、修改或移除新项时,RealmResults对象都会自动更新,因此在添加和插入批量项时调用 notifyDataSetChanged。此时,您可能会担心不会触发的动画,因为您正在调用 notifyDataSetChanged而不是 notifyXXX方法。这就是为什么我让 getItemId方法返回 result 对象中每一行的时间戳。如果您调用 setHasStableIds(true)并覆盖 getItemId以提供位置以外的其他内容,那么使用 notifyDataSetChanged可以在两个步骤中实现动画。

第五步

让我们将 RecyclerView添加到我们的 ActivityFragment。在我的例子中,我使用的是 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,我没有张贴在这个简短的答案。

第六步

用代码构造 RecyclerView并提供它所需要的数据。在 onCreate中创建并初始化一个 realmentobject,然后在 onDestroy中关闭它,就像关闭一个 SQLiteOpenHelper实例一样。在最简单的 onCreate内的 Activity将看起来像这样。initUi方法是所有奇迹发生的地方。我在 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);
}

注意,在第一个步骤中,我查询畴,以异步方式提供 Data类中按变量名称称为 data 排序的所有对象。这给了我一个 RealmResults对象,主线程上有0个项目,我正在 Adapter上设置这些项目。我添加了一个 RealmChangeListener,以便在数据从后台线程加载完成时通知我,我在后台线程中用我的 Adapter调用 notifyDataSetChanged。我还将 setHasStableIds调用为 true,以便让 RecyclerView.Adapter实现跟踪添加、删除或修改的项目。对于我的 ActivityonDestroy关闭“王国”实例

@Override
protected void onDestroy() {
super.onDestroy();
mRealm.close();
}

这个方法 initUi可以在 ActivityonCreateViewFragmentonCreateonViewCreated中调用。请注意以下事项。

第七步

砰!RecyclerView中有来自数据库的数据异步加载,没有带动画的 CursorLoaderCursorAdapterSQLiteOpenHelper。这里显示的 GIF 图像有点滞后,但是当您添加或删除项目时,动画正在发生。

data from database inside RecyclerView

我使用 分类列表作为后端,扩展 回收视图。适配器制作了一个 游标适配器

可以与 SQLiteCursor装载机一起使用

只是另一个答案,因为我不喜欢接受的一个(这是我没有一个直观的用法)。

下面是我自己的实现,它与 SimpleCursorAdapter非常相似(部分灵感来自 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

您可以使用 SQLite 数据库存储详细信息。 你可以在 github 上查看我的代码。 < a href = “ https://github.com/Thiru-wta/ToDo”rel = “ nofollow norefrer”> 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;


}

从数据库中检索数据并存储在一个列表中之后,也许应该是这样的:

    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);

下面是我为一个回收视图实现的 cursorAdapter。 它支持 OnItemClickListener、 OnLongItemClickListener、 OnfooterClickListener、 Sections 和 footer。

只需扩展这个适配器并创建您自己的适配器。重写提供的方法。如果已经创建了适配器,那么从 OnCursorLoadFinish 方法中传递游标,然后 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;
}




}

最简单的实现是在适配器中使用游标对象,并将游标传递给视图持有者构造函数,以修改视图并更改 onBindViewholder 中的游标位置。

如果使用游标 Loader,请在 onLoadfinished()中调用 setCursor 方法,并在 onLoadReset()中传递 null

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);
}
}

}

最后给出了一个数据库/网络适配器的实现。

它通过安卓 架构组件实现了这一点:

Room Database: SQLite 数据库之上的数据库层,用于处理用 SQLiteOpenHelper处理的普通任务。作为基础 SQLite 数据库访问点的数据库持有者。Room 数据库使用 DAO 向 SQLite 数据库发出查询。

ViewModel: 为 UI 提供数据。作为仓库和 UI 之间的通信中心。隐藏数据来自 UI 的位置。ViewModel 实例在活动/片段再创建中存活。

LiveData: 可以观察到的数据持有者类。始终保存/缓存最新版本的数据。数据更改时通知其观察者。LiveData 了解生命周期。UI 组件只观察相关数据,不停止或恢复观察。LiveData 会自动管理所有这些,因为它在观察时会意识到相关的生命周期状态变化。