Android RecyclerView+;游标装载机+;内容提供者+&引用;“加载更多”;
我创建了一个活动,因为我正在为从数据库加载数据实现CursorLoader 我已经对该表的所有记录执行了该操作,但我希望加载30-30条记录,如加载更多功能Android RecyclerView+;游标装载机+;内容提供者+&引用;“加载更多”;,android,android-contentprovider,android-cursoradapter,android-cursor,android-cursorloader,Android,Android Contentprovider,Android Cursoradapter,Android Cursor,Android Cursorloader,我创建了一个活动,因为我正在为从数据库加载数据实现CursorLoader 我已经对该表的所有记录执行了该操作,但我希望加载30-30条记录,如加载更多功能 import android.content.ContentValues; import android.database.Cursor; import android.database.MatrixCursor; import android.os.Bundle; import android.os.Handler; import and
import android.content.ContentValues;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.widget.Toast;
import com.example.pagingproject.adapters.CustomCursorRecyclerViewAdapter;
import com.example.pagingproject.databases.RequestProvider;
import com.example.pagingproject.databases.TableItems;
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
public final int offset = 30;
private int page = 0;
private RecyclerView mRecyclerView;
private boolean loadingMore = false;
private Toast shortToast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
CustomCursorRecyclerViewAdapter mAdapter = new CustomCursorRecyclerViewAdapter(this, null);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
int itemsCountLocal = getItemsCountLocal();
if (itemsCountLocal == 0) {
fillTestElements();
}
shortToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
int maxPositions = layoutManager.getItemCount();
if (lastVisibleItemPosition == maxPositions - 1) {
if (loadingMore)
return;
loadingMore = true;
page++;
getSupportLoaderManager().restartLoader(0, null, MainActivity.this);
}
}
});
getSupportLoaderManager().restartLoader(0, null, this);
}
private void fillTestElements() {
int size = 1000;
ContentValues[] cvArray = new ContentValues[size];
for (int i = 0; i < cvArray.length; i++) {
ContentValues cv = new ContentValues();
cv.put(TableItems.TEXT, ("text " + i));
cvArray[i] = cv;
}
getContentResolver().bulkInsert(RequestProvider.urlForItems(0), cvArray);
}
private int getItemsCountLocal() {
int itemsCount = 0;
Cursor query = getContentResolver().query(RequestProvider.urlForItems(0), null, null, null, null);
if (query != null) {
itemsCount = query.getCount();
query.close();
}
return itemsCount;
}
/*loader*/
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
case 0:
return new CursorLoader(this, RequestProvider.urlForItems(offset * page), null, null, null, null);
default:
throw new IllegalArgumentException("no id handled!");
}
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
switch (loader.getId()) {
case 0:
Log.d(TAG, "onLoadFinished: loading MORE");
shortToast.setText("loading MORE " + page);
shortToast.show();
Cursor cursor = ((CustomCursorRecyclerViewAdapter) mRecyclerView.getAdapter()).getCursor();
//fill all exisitng in adapter
MatrixCursor mx = new MatrixCursor(TableItems.Columns);
fillMx(cursor, mx);
//fill with additional result
fillMx(data, mx);
((CustomCursorRecyclerViewAdapter) mRecyclerView.getAdapter()).swapCursor(mx);
handlerToWait.postDelayed(new Runnable() {
@Override
public void run() {
loadingMore = false;
}
}, 2000);
break;
default:
throw new IllegalArgumentException("no loader id handled!");
}
}
private Handler handlerToWait = new Handler();
private void fillMx(Cursor data, MatrixCursor mx) {
if (data == null)
return;
data.moveToPosition(-1);
while (data.moveToNext()) {
mx.addRow(new Object[]{
data.getString(data.getColumnIndex(TableItems._ID)),
data.getString(data.getColumnIndex(TableItems.TEXT))
});
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// TODO: 2016-10-13
}
//
private static final String TAG = "MainActivity";
}
我试图创建查询并加载前30条记录,但我无法理解如何请求新记录
我的活动代码如下:
public class ProductListActivity extends AppCompatActivity
implements LoaderManager.LoaderCallbacks<Cursor> {
/**
* Records in list
*/
int offset = 0;
/**
* For Current Activity *
*/
Context mContext;
/**
* Activity Binding
*/
ActivityProductListBinding activityProductListBinding;
/**
* Product Adapter
*/
ProductListAdapter productListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* DataBinding with XML
*/
activityProductListBinding = DataBindingUtil.setContentView(this, R.layout.activity_product_list);
/**
* Getting Context
*/
mContext = getApplicationContext();
/***
* TOOLBAR Settings...
*/
setSupportActionBar(activityProductListBinding.toolbar);
activityProductListBinding.toolbarTitleTextview.setText(R.string.string_title_products);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
final ActionBar ab = getSupportActionBar();
if (ab != null)
ab.setDisplayHomeAsUpEnabled(true);
/**
* RecyclerView Setup
*/
GridLayoutManager manager = new GridLayoutManager(this, 2);
activityProductListBinding.productListRecyclerView.setLayoutManager(manager);
/**
* First Time init Loader
*/
getSupportLoaderManager().initLoader(1, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
final Uri CONTENT_URI = KOOPSContentProvider.CONTENT_URI_PRODUCT.buildUpon()
.appendQueryParameter(KOOPSContentProvider.QUERY_PARAMETER_OFFSET,
String.valueOf(offset))
.build();
return new CursorLoader(this, CONTENT_URI ,null, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
//When the loader has loaded some data (either initially, or the
//datasource has changed and a new cursor is being provided),
//Then we'll swap out the cursor in our recyclerview's adapter
// and we'll create the adapter if necessary
Log.d(LogUtils.TAG, "Cursor : " + data.getCount());
if (productListAdapter == null) {
productListAdapter = new ProductListAdapter(this, data);
activityProductListBinding.productListRecyclerView.setAdapter(productListAdapter);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
//If the loader is reset, we need to clear out the
//current cursor from the adapter.
productListAdapter.reQuery(null);
}
}
我已尝试在适配器中合并光标,但出现错误:
FATAL EXCEPTION: main
Process: com.kevalam.koopsv3, PID: 25021
java.lang.IllegalStateException: Observer android.database.MergeCursor$1@570f82d is already registered.
at android.database.Observable.registerObserver(Observable.java:49)
at android.database.AbstractCursor.registerDataSetObserver(AbstractCursor.java:358)
at android.database.CursorWrapper.registerDataSetObserver(CursorWrapper.java:222)
at android.database.MergeCursor.<init>(MergeCursor.java:50)
at com.kevalam.koops.adapter.ProductListAdapter.mergeCursor(ProductListAdapter.java:71)
at com.kevalam.koops.ui.ProductListActivity.onLoadFinished(ProductListActivity.java:161)
at com.kevalam.koops.ui.ProductListActivity.onLoadFinished(ProductListActivity.java:38)
致命异常:main
进程:com.kevalam.koopsv3,PID:25021
java.lang.IllegalStateException:Observer android.database.MergeCursor$1@570f82d已注册。
在android.database.Observable.registerObserver上(Observable.java:49)
位于android.database.AbstractCursor.registerDataSetObserver(AbstractCursor.java:358)
位于android.database.CursorWrapper.registerDataSetObserver(CursorWrapper.java:222)
位于android.database.MergeCursor.(MergeCursor.java:50)
位于com.kevalam.koops.adapter.ProductListAdapter.mergeCursor(ProductListAdapter.java:71)
位于com.kevalam.koops.ui.ProductListActivity.onLoadFinished(ProductListActivity.java:161)
位于com.kevalam.koops.ui.ProductListActivity.onLoadFinished(ProductListActivity.java:38)
已编辑(适配器代码):
公共类ProductListAdapter扩展了RecyclerView.Adapter{
//因为当前形式的RecyclerView.Adapter本机不可用
//支持游标,我们包装了一个游标适配器来完成所有的工作
//对我们来说。
游标适配器mCursorAdapter;
活动mContext;
随机rnd;
公共产品列表适配器(AppCompatActivity上下文,游标c){
mContext=上下文;
rnd=新随机数();
mCursorAdapter=新游标适配器(mContext,c,0){
@凌驾
公共视图newView(上下文上下文、光标、视图组父对象){
//把这里的景色放大
LayoutFlater充气器=(LayoutFlater)context.getSystemService(context.LAYOUT\u充气器\u服务);
返回充气机。充气(R.layout.row\u product\u layout\u grid,parent,false);
}
@凌驾
公共void bindView(视图、上下文上下文、光标){
String productName=cursor.getString(cursor.getColumnIndex(产品名称));
//绑定操作
((TextView)view.findViewById(R.id.sub\u product\u name\u text\u view)).setText(productName);
int color=color.argb(200,rnd.nextInt(256),rnd.nextInt(256),rnd.nextInt(256));
字符串url=”http://dummyimage.com/300/“+color+”/ffffff&text=“+(cursor.getPosition()+1);
毕加索
.与(上下文)
.load(url)
.placeholder(R.mipmap.ic_launcher)//也可以是可绘制的
.into((ImageView)view.findViewById(R.id.sub_产品_图像_视图));
}
};
}
公共无效合并光标(光标c){
if(mCursorAdapter!=null){
Cursor[]cursorArray={mCursorAdapter.getCursor(),c};
MergeCursor MergeCursor=新的MergeCursor(cursorArray);
重新查询(合并光标);
}
}
公共作废重新查询(光标c){
if(mCursorAdapter!=null){
mCursorAdapter.changeCursor(c);
mCursorAdapter.notifyDataSetChanged();
notifyDataSetChanged();
}
}
@凌驾
public int getItemCount(){
返回mCursorAdapter.getCount();
}
@凌驾
公共无效onBindViewHolder(ViewHolder,int位置){
//将绑定操作传递给游标加载器
mCursorAdapter.getCursor().moveToPosition(position);//已编辑:根据下面的注释中的建议添加了此行,谢谢:)
bindView(holder.view、mContext、mCursorAdapter.getCursor());
}
@凌驾
public ViewHolder onCreateViewHolder(视图组父级,int-viewType){
//将充气机作业传递给光标适配器
视图v=mCursorAdapter.newView(mContext,mCursorAdapter.getCursor(),父级);
返回新的视图持有者(v);
}
公共静态类ViewHolder扩展了RecyclerView.ViewHolder{
视图;
公共视图持有者(视图项视图){
超级(项目视图);
视图=itemView.findViewById(R.id.product\u row\u card\u视图);
}
}
}
任何人都可以帮忙,请提前感谢。最近,我创建了一个TodoApp,其中包含了大多数必需的功能。 该应用程序包含以下相关功能: 1) 自定义
RecyclerView
,支持光标
2) 内容提供程序对SQLite数据库执行基本CRUD操作
3) 轻松与内容提供商交互
4) CursorLoader
,它在基础数据库发生更改时立即更新RecyclerView
剩下的就是实现更多的加载功能。Codepath有一篇非常好的文章。(如果需要任何帮助,请告诉我:)更新01.10.2017
谷歌宣布了新的分页库,更多信息请点击此处
这是一个基于cursoradapter+recyclerview+provider的分页工作示例
我给你一步一步的代码+奖金与gif预览
但游标适配器上的IMHO分页毫无意义,因为db正在处理所有繁重的内容,并加载更多数据:)
第1步。创建数据库:
public class CustomSqliteOpenHelper extends SQLiteOpenHelper {
private static final String TAG = "CustomSqliteOpenHelper";
public CustomSqliteOpenHelper(Context context) {
super(context, "db.db", null, 1);
}
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TableItems.CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(TableItems.DROP_TABLE);
onCreate(db);
}
}
第2步。创建表格
public class TableItems {
public static final String NAME = TableItems.class.getSimpleName().toLowerCase();
public static final String _ID = "_id";
public static final String TEXT = "text";
public static final String CREATE_TABLE =
"CREATE TABLE " + NAME +
" ( " +
_ID + " integer primary key autoincrement, " +
TEXT + " text " +
" ); ";
public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + NAME;
public static String[] Columns = new String[]{_ID, TEXT};
}
第3步。创建提供程序
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.util.Log;
import com.example.pagingproject.BuildConfig;
public class RequestProvider extends ContentProvider {
private static final String TAG = "RequestProvider";
private SQLiteOpenHelper mSqliteOpenHelper;
private static final UriMatcher sUriMatcher;
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".db";
private static final int
TABLE_ITEMS = 0;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, TableItems.NAME + "/offset/" + "#", TABLE_ITEMS);
}
public static Uri urlForItems(int limit) {
return Uri.parse("content://" + AUTHORITY + "/" + TableItems.NAME + "/offset/" + limit);
}
@Override
public boolean onCreate() {
mSqliteOpenHelper = new CustomSqliteOpenHelper(getContext());
return true;
}
@Override
synchronized public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mSqliteOpenHelper.getReadableDatabase();
SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();
Cursor c = null;
String offset;
switch (sUriMatcher.match(uri)) {
case TABLE_ITEMS: {
sqb.setTables(TableItems.NAME);
offset = uri.getLastPathSegment();
break;
}
default:
throw new IllegalArgumentException("uri not recognized!");
}
int intOffset = Integer.parseInt(offset);
String limitArg = intOffset + ", " + 30;
Log.d(TAG, "query: " + limitArg);
c = sqb.query(db, projection, selection, selectionArgs, null, null, sortOrder, limitArg);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public String getType(@NonNull Uri uri) {
return BuildConfig.APPLICATION_ID + ".item";
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
String table = "";
switch (sUriMatcher.match(uri)) {
case TABLE_ITEMS: {
table = TableItems.NAME;
break;
}
}
long result = mSqliteOpenHelper.getWritableDatabase().insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE);
if (result == -1) {
throw new SQLException("insert with conflict!");
}
Uri retUri = ContentUris.withAppendedId(uri, result);
return retUri;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
return -1;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return -1;
}
}
import android.content.Context;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
/**
* Created by skyfishjy on 10/31/14.
*/
public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
protected Context mContext;
private Cursor mCursor;
private boolean mDataValid;
private int mRowIdColumn;
private DataSetObserver mDataSetObserver;
public CursorRecyclerViewAdapter(Context context, Cursor cursor) {
mContext = context;
mCursor = cursor;
mDataValid = cursor != null;
mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1;
mDataSetObserver = new NotifyingDataSetObserver(this);
if (mCursor != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
}
public Cursor getCursor() {
return mCursor;
}
@Override
public int getItemCount() {
if (mDataValid && mCursor != null) {
return mCursor.getCount();
}
return 0;
}
@Override
public long getItemId(int position) {
if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
return mCursor.getLong(mRowIdColumn);
}
return 0;
}
@Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(true);
}
public static final String TAG = CursorRecyclerViewAdapter.class.getSimpleName();
public abstract void onBindViewHolder(VH viewHolder, Cursor cursor);
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(VH viewHolder, int position) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
onBindViewHolder(viewHolder, mCursor);
}
/**
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*/
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
if (old != null) {
old.close();
}
}
/**
* Swap in a new Cursor, returning the old Cursor. Unlike
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
* closed.
*/
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
final Cursor oldCursor = mCursor;
if (oldCursor != null && mDataSetObserver != null) {
oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (mCursor != null) {
if (mDataSetObserver != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
mRowIdColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
notifyDataSetChanged();
} else {
mRowIdColumn = -1;
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
return oldCursor;
}
public void setDataValid(boolean mDataValid) {
this.mDataValid = mDataValid;
}
private class NotifyingDataSetObserver extends DataSetObserver {
private RecyclerView.Adapter adapter;
public NotifyingDataSetObserver(RecyclerView.Adapter adapter) {
this.adapter = adapter;
}
@Override
public void onChanged() {
super.onChanged();
((CursorRecyclerViewAdapter) adapter).setDataValid(true);
adapter.notifyDataSetChanged();
}
@Override
public void onInvalidated() {
super.onInvalidated();
((CursorRecyclerViewAdapter) adapter).setDataValid(false);
}
}
}
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
public class CustomViewHolder extends RecyclerView.ViewHolder {
public TextView textView1;
public CustomViewHolder(View itemView) {
super(itemView);
textView1 = (TextView) itemView.findViewById(android.R.id.text1);
}
public void setData(Cursor c) {
textView1.setText(c.getString(c.getColumnIndex("text")));
}
}
第4步。创建一个抽象的游标适配器,我从StackOverflow自定义游标回收视图适配器中获取了示例
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.util.Log;
import com.example.pagingproject.BuildConfig;
public class RequestProvider extends ContentProvider {
private static final String TAG = "RequestProvider";
private SQLiteOpenHelper mSqliteOpenHelper;
private static final UriMatcher sUriMatcher;
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".db";
private static final int
TABLE_ITEMS = 0;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, TableItems.NAME + "/offset/" + "#", TABLE_ITEMS);
}
public static Uri urlForItems(int limit) {
return Uri.parse("content://" + AUTHORITY + "/" + TableItems.NAME + "/offset/" + limit);
}
@Override
public boolean onCreate() {
mSqliteOpenHelper = new CustomSqliteOpenHelper(getContext());
return true;
}
@Override
synchronized public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mSqliteOpenHelper.getReadableDatabase();
SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();
Cursor c = null;
String offset;
switch (sUriMatcher.match(uri)) {
case TABLE_ITEMS: {
sqb.setTables(TableItems.NAME);
offset = uri.getLastPathSegment();
break;
}
default:
throw new IllegalArgumentException("uri not recognized!");
}
int intOffset = Integer.parseInt(offset);
String limitArg = intOffset + ", " + 30;
Log.d(TAG, "query: " + limitArg);
c = sqb.query(db, projection, selection, selectionArgs, null, null, sortOrder, limitArg);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public String getType(@NonNull Uri uri) {
return BuildConfig.APPLICATION_ID + ".item";
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
String table = "";
switch (sUriMatcher.match(uri)) {
case TABLE_ITEMS: {
table = TableItems.NAME;
break;
}
}
long result = mSqliteOpenHelper.getWritableDatabase().insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE);
if (result == -1) {
throw new SQLException("insert with conflict!");
}
Uri retUri = ContentUris.withAppendedId(uri, result);
return retUri;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
return -1;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return -1;
}
}
import android.content.Context;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
/**
* Created by skyfishjy on 10/31/14.
*/
public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
protected Context mContext;
private Cursor mCursor;
private boolean mDataValid;
private int mRowIdColumn;
private DataSetObserver mDataSetObserver;
public CursorRecyclerViewAdapter(Context context, Cursor cursor) {
mContext = context;
mCursor = cursor;
mDataValid = cursor != null;
mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1;
mDataSetObserver = new NotifyingDataSetObserver(this);
if (mCursor != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
}
public Cursor getCursor() {
return mCursor;
}
@Override
public int getItemCount() {
if (mDataValid && mCursor != null) {
return mCursor.getCount();
}
return 0;
}
@Override
public long getItemId(int position) {
if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
return mCursor.getLong(mRowIdColumn);
}
return 0;
}
@Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(true);
}
public static final String TAG = CursorRecyclerViewAdapter.class.getSimpleName();
public abstract void onBindViewHolder(VH viewHolder, Cursor cursor);
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(VH viewHolder, int position) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
onBindViewHolder(viewHolder, mCursor);
}
/**
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*/
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
if (old != null) {
old.close();
}
}
/**
* Swap in a new Cursor, returning the old Cursor. Unlike
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
* closed.
*/
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
final Cursor oldCursor = mCursor;
if (oldCursor != null && mDataSetObserver != null) {
oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (mCursor != null) {
if (mDataSetObserver != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
mRowIdColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
notifyDataSetChanged();
} else {
mRowIdColumn = -1;
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
return oldCursor;
}
public void setDataValid(boolean mDataValid) {
this.mDataValid = mDataValid;
}
private class NotifyingDataSetObserver extends DataSetObserver {
private RecyclerView.Adapter adapter;
public NotifyingDataSetObserver(RecyclerView.Adapter adapter) {
this.adapter = adapter;
}
@Override
public void onChanged() {
super.onChanged();
((CursorRecyclerViewAdapter) adapter).setDataValid(true);
adapter.notifyDataSetChanged();
}
@Override
public void onInvalidated() {
super.onInvalidated();
((CursorRecyclerViewAdapter) adapter).setDataValid(false);
}
}
}
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
public class CustomViewHolder extends RecyclerView.ViewHolder {
public TextView textView1;
public CustomViewHolder(View itemView) {
super(itemView);
textView1 = (TextView) itemView.findViewById(android.R.id.text1);
}
public void setData(Cursor c) {
textView1.setText(c.getString(c.getColumnIndex("text")));
}
}
第6步。创建自定义视图夹
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.util.Log;
import com.example.pagingproject.BuildConfig;
public class RequestProvider extends ContentProvider {
private static final String TAG = "RequestProvider";
private SQLiteOpenHelper mSqliteOpenHelper;
private static final UriMatcher sUriMatcher;
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".db";
private static final int
TABLE_ITEMS = 0;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, TableItems.NAME + "/offset/" + "#", TABLE_ITEMS);
}
public static Uri urlForItems(int limit) {
return Uri.parse("content://" + AUTHORITY + "/" + TableItems.NAME + "/offset/" + limit);
}
@Override
public boolean onCreate() {
mSqliteOpenHelper = new CustomSqliteOpenHelper(getContext());
return true;
}
@Override
synchronized public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mSqliteOpenHelper.getReadableDatabase();
SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();
Cursor c = null;
String offset;
switch (sUriMatcher.match(uri)) {
case TABLE_ITEMS: {
sqb.setTables(TableItems.NAME);
offset = uri.getLastPathSegment();
break;
}
default:
throw new IllegalArgumentException("uri not recognized!");
}
int intOffset = Integer.parseInt(offset);
String limitArg = intOffset + ", " + 30;
Log.d(TAG, "query: " + limitArg);
c = sqb.query(db, projection, selection, selectionArgs, null, null, sortOrder, limitArg);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public String getType(@NonNull Uri uri) {
return BuildConfig.APPLICATION_ID + ".item";
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
String table = "";
switch (sUriMatcher.match(uri)) {
case TABLE_ITEMS: {
table = TableItems.NAME;
break;
}
}
long result = mSqliteOpenHelper.getWritableDatabase().insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE);
if (result == -1) {
throw new SQLException("insert with conflict!");
}
Uri retUri = ContentUris.withAppendedId(uri, result);
return retUri;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
return -1;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return -1;
}
}
import android.content.Context;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
/**
* Created by skyfishjy on 10/31/14.
*/
public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
protected Context mContext;
private Cursor mCursor;
private boolean mDataValid;
private int mRowIdColumn;
private DataSetObserver mDataSetObserver;
public CursorRecyclerViewAdapter(Context context, Cursor cursor) {
mContext = context;
mCursor = cursor;
mDataValid = cursor != null;
mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1;
mDataSetObserver = new NotifyingDataSetObserver(this);
if (mCursor != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
}
public Cursor getCursor() {
return mCursor;
}
@Override
public int getItemCount() {
if (mDataValid && mCursor != null) {
return mCursor.getCount();
}
return 0;
}
@Override
public long getItemId(int position) {
if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
return mCursor.getLong(mRowIdColumn);
}
return 0;
}
@Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(true);
}
public static final String TAG = CursorRecyclerViewAdapter.class.getSimpleName();
public abstract void onBindViewHolder(VH viewHolder, Cursor cursor);
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(VH viewHolder, int position) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
onBindViewHolder(viewHolder, mCursor);
}
/**
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*/
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
if (old != null) {
old.close();
}
}
/**
* Swap in a new Cursor, returning the old Cursor. Unlike
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
* closed.
*/
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
final Cursor oldCursor = mCursor;
if (oldCursor != null && mDataSetObserver != null) {
oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (mCursor != null) {
if (mDataSetObserver != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
mRowIdColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
notifyDataSetChanged();
} else {
mRowIdColumn = -1;
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
return oldCursor;
}
public void setDataValid(boolean mDataValid) {
this.mDataValid = mDataValid;
}
private class NotifyingDataSetObserver extends DataSetObserver {
private RecyclerView.Adapter adapter;
public NotifyingDataSetObserver(RecyclerView.Adapter adapter) {
this.adapter = adapter;
}
@Override
public void onChanged() {
super.onChanged();
((CursorRecyclerViewAdapter) adapter).setDataValid(true);
adapter.notifyDataSetChanged();
}
@Override
public void onInvalidated() {
super.onInvalidated();
((CursorRecyclerViewAdapter) adapter).setDataValid(false);
}
}
}
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
public class CustomViewHolder extends RecyclerView.ViewHolder {
public TextView textView1;
public CustomViewHolder(View itemView) {
super(itemView);
textView1 = (TextView) itemView.findViewById(android.R.id.text1);
}
public void setData(Cursor c) {
textView1.setText(c.getString(c.getColumnIndex("text")));
}
}
第7步。用sa编写代码