-
-
Save skyfishjy/443b7448f59be978bc59 to your computer and use it in GitHub Desktop.
/* | |
* Copyright (C) 2014 skyfish.jy@gmail.com | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
import android.content.Context; | |
import android.database.Cursor; | |
import android.database.DataSetObserver; | |
import android.support.v7.widget.RecyclerView; | |
/** | |
* Created by skyfishjy on 10/31/14. | |
*/ | |
public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> { | |
private 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(); | |
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 abstract void onBindViewHolder(VH viewHolder, Cursor cursor); | |
@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; | |
} | |
private class NotifyingDataSetObserver extends DataSetObserver { | |
@Override | |
public void onChanged() { | |
super.onChanged(); | |
mDataValid = true; | |
notifyDataSetChanged(); | |
} | |
@Override | |
public void onInvalidated() { | |
super.onInvalidated(); | |
mDataValid = false; | |
notifyDataSetChanged(); | |
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter | |
} | |
} | |
} |
import android.content.Context; | |
import android.database.Cursor; | |
import android.support.v7.widget.RecyclerView; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.widget.ImageView; | |
import android.widget.TextView; | |
import java.util.List; | |
/** | |
* Created by skyfishjy on 10/31/14. | |
*/ | |
public class MyListCursorAdapter extends CursorRecyclerViewAdapter<MyListCursorAdapter.ViewHolder>{ | |
public MyListCursorAdapter(Context context,Cursor cursor){ | |
super(context,cursor); | |
} | |
public static class ViewHolder extends RecyclerView.ViewHolder { | |
public TextView mTextView; | |
public ViewHolder(View view) { | |
super(view); | |
mTextView = view.findViewById(R.id.text); | |
} | |
} | |
@Override | |
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | |
View itemView = LayoutInflater.from(parent.getContext()) | |
.inflate(R.layout.my_view, parent, false); | |
ViewHolder vh = new ViewHolder(itemView); | |
return vh; | |
} | |
@Override | |
public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) { | |
MyListItem myListItem = MyListItem.fromCursor(cursor); | |
viewHolder.mTextView.setText(myListItem.getName()); | |
} | |
} |
public class MyListItem{ | |
private String name; | |
public void setName(String name){ | |
this.name=name; | |
} | |
public String getName(){ | |
return name; | |
} | |
public static MyListItem fromCursor(Cursor cursor) { | |
//TODO return your MyListItem from cursor. | |
} | |
} |
Instead of overwriting setHasStableIds() better call it in the constructor. As it is now, it's only called when the extending class calls setHasStableIds().
When setHasStableIds() is set to true and getItemId() returns unique ids, change animations are shown.
@yaseen2591: multi viewtypes.
In CursorRecyclerViewAdapter, you add:
@OverRide
public int getItemViewType(int position) {
if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
return mCursor.getInt(mCursor.getColumnIndex(NotificationTable.COL_TYPE));
}
return 0;
}
and In NotificationCursorAdapter, you modify:
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
switch (viewType) {
case TYPE_1:
break;
case TYPE_2:
break;
}
}
I create an exteionsion that supports section for this cursor adapter. Tested for ListView only.
How do you close cursor? After setting an adapter?
Hi for all.
Your can delete context from your adapter, because you don't use it in adapter, add RecyclerView don't want Context for work. Why you include Context in your code?
What should I do when I delete item form database by getWritableDatabase().delete() ? Now I have strange behavior: the onBindViewHolder is called with strange cursor.
please help me with the implementation part,can you give an example program how to implement this...?
https://omaakaar.github.io/Hello-World/
need help to take benefit for and from github.
I am newbie and want to make money online.
Hi! Thank you very much for this class, he helped me a lot!
hey ,
what inside in fromCursor method
public static MyListItem fromCursor(Cursor cursor) {
//TODO return your MyListItem from cursor.
}
Why holding context? We can always get it from parent.getContext()
Any example on how to use it with SQLite db file in assets
folder. I just want to retrieve it and display with RecyclerView
using above CursorLoader
.
thank you @lonely1990
zishanj do you found your answer abov question. i want in my project display data with RecyclerView using above CursorLoader from .db file asset folder
Hey,
Where do you call on click? Cause the implementation doesn't trigger it, and when I call it in the onBind method with the cursor, the if is wrong.. Any ideas? Thanks!
I am facing some problem with this code. I am populating some items in recyclerview using above cursor adapter. But after scrolling, i am getting wrong cursor position on click event. Sometimes it work fine, sometime mislead the cursor by 1 or 2 positions
Same problem here. Will try to search a solution tomorrow. Does anyone else know a solution?
public static MyListItem fromCursor(Cursor cursor) {
MyListItem listItem = new MyListItem();
listItem.setName(cursor.getString(cursor.getColumnIndex(STUDENT_NAME)));
return listItem;
}
How to sort items in ascending and descending order?
I still don't get the last part, where you used the holder.mTextView.setText part to set text, MyListItem.fromCursor(cursor); to get the data, but how did you get the each data from the cursor, can you explain better? thanks
I'm confused with this. I added my layout file for list item to the onCreateViewHolder(ViewGroup parent, int viewType)
method.
Then I instantiated the adapter by calling adapter = new MyListCursorAdapter(getContext(), cursor);
,
instantiated my RecyclerView: recyclerView = v.findViewById(R.id.my_recycler_view);
and then I set the adapter to the recycler view: recyclerView.setAdapter(adapter);
.
What am I missing? The RecyclerView with items still doesn't show up. I added a log to the fromCursor(Cursor cursor)
method in MyListItem
class to see if the method is getting triggered, and no logs appear - so the fromCursor method is never called. Should I somewhere explicitly call e.g. the onBindViewHolder(ViewHolder viewHolder, Cursor cursor)
method? What would be the ViewHolder - my list item?
Actually, a simple working example would be extremely helpful. I just need a guide how to "connect" the solution to my app. For comparison - when I tried using ListView and SimpleCursorAdapter to display data from Cursor, I only had to instantiate the SimpleCursorAdapter, the ListView and set the adapter to the ListView - and that was it, it was working. This solution requires me to do something more it seems, and I just can't wrap my head around it.
Edit: Nevermind, I forgot to set layout manager to the RecyclerView. I got it working now, thanks.
work flawlessly
android.support.v7.util.AsyncListUtil
can also be simple to implement.
Hello, first of all many thanks for sharing this code! Can you help me with a problem please? I have an Activity with a RecyclerView that uses an AsyncTaskLoader to load data from the database and update MyListCursorAdapter with cursor. All works fine until I delete a row from the database using ContentProvider, the adapter don't refresh and the RecyclerView that continue to show the item I deleted with a ContextMenu.
Any idea?
@skyfishjy
thanks for the example
@PRosenb
Thanks for the help. Now everything works fine ))
Thank you so much for this code! The example usage was also incredibly helpful.
I wonder if we can notifyChanged
just for certain range, maybe we can have a lastBindIdx
and compare idx within 10 item. It will make the animation work properly.
Thank you so much for this code!
Hi! Can you help me to update my adapter with yours?
Me too
How can I extend this CursorRecyclerViewAdapter.java to RecyclerViews that has multiple Viewtypes??