I'm using ActiveAndroid ORM and UltimateRecyclerView in my Android application. I don't know how to display a data in a list using this popular libraries. I know, the data is saved to database correctly. Using the following method, I'm getting a cursor to the data I want to display.
public Cursor eventsCursor() {
// `Event` is an ActiveAndroid model class
String query = new Select()
.from(Event.class)
.orderBy("time")
.toSql();
return ActiveAndroid.getDatabase().rawQuery(query, null);
}
I wrote my RecyclerView.Adapter<VH>
implementation (EventCursorRecyclerAdapter
) compatible with UltimateViewAdapter, that holds a CursorAdapter member in order to work with database (code is based on this solution). There is no ContentObservers in ActiveAndroid (issue #3), so I register an observer on ContentProvider during initialization. The initialization looks like:
Fragment class
protected UltimateRecyclerView listView;
private AbstractCursorRecyclerAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
cursor = eventsCursor();
cursor.setNotificationUri(getActivity().getContentResolver(),
ContentProvider.createUri(Event.class, null));
adapter = new EventCursorRecyclerAdapter(getActivity(), cursor, R.layout.list_item_event);
getActivity().getSupportLoaderManager().initLoader(0, savedInstanceState, this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/* ... */
// Configure RecyclerView
listView.setLayoutManager(new LinearLayoutManager(getActivity()));
listView.setAdapter(adapter);
}
I used the ContentProvider of ActiveAndroid (docs) to create URI for CursorLoader:
AndroidManifest.xml
<application ...>
<provider
android:name="com.activeandroid.content.ContentProvider"
android:authorities="com.activeandroid.content.ContentProvider"
android:exported="false" />
...
</application>
LoaderManager.LoaderCallbacks<T>
implementation
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle cursor) {
return new CursorLoader(
MyFragment.this.getActivity(),
ContentProvider.createUri(Event.class, null),
null, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.asCursorAdapter().changeCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.asCursorAdapter().changeCursor(null);
}
When I detect a change in the dataset, I call
MyFragment.this.getActivity().getContentResolver().notifyChange(
ContentProvider.createUri(Event.class, modifiedId), null);
I don't know why, but it's not working. No data is displayed, with no errors logged.
UPDATE Adapter's full code (reminds a matryoshka).
EventCursorAdapter
public class EventCursorAdapter extends CursorAdapter {
private final int itemLayoutId;
private final LayoutInflater inflater;
/**
* @param itemLayoutId List item layout
*/
public EventCursorAdapter(Context context, Cursor cursor, @LayoutRes int itemLayoutId) {
super(context, cursor, 0);
this.itemLayoutId = itemLayoutId;
this.inflater = LayoutInflater.from(context);
}
/**
* It is used to inflate a new view and return it.
*
* @return a new View
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return inflater.inflate(itemLayoutId, parent, false);
}
/**
* Bind all data to a given view (such as setting the text on a TextView).
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
EventItemViewHolder holder = (EventItemViewHolder) view.getTag();
// Extract properties and populate fields
holder.tvName.setText(cursor.getString(holder.nameIndex));
/* ... */
}
}
EventCursorRecyclerAdapter
public final class EventCursorRecyclerAdapter extends AbstractCursorRecyclerAdapter {
/**
* @param itemLayoutId List item layout
*/
public EventCursorRecyclerAdapter(Context context, Cursor cursor, @LayoutRes int itemLayoutId) {
super(new EventCursorAdapter(context, cursor, itemLayoutId), context);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// Passing the binding operation to cursor loader
cursorAdapter.bindView(holder.itemView, context, cursorAdapter.getCursor());
}
@Override
public UltimateRecyclerviewViewHolder onCreateViewHolder(ViewGroup parent) {
// Passing the inflater job to the cursor-adapter
View v = cursorAdapter.newView(context, cursorAdapter.getCursor(), parent);
return new EventItemViewHolder(v, cursorAdapter.getCursor());
}
/**
* See ViewHolder Pattern.
*/
public final class EventItemViewHolder extends UltimateRecyclerviewViewHolder {
// Column indices
int nameIndex, timeIndex /*, ...*/;
// Views
TextView tvName, tvTime;
/* ... */
public EventItemViewHolder(View view, Cursor cursor) {
super(view);
// Find fields to populate in inflated template
tvName = (TextView) view.findViewById(R.id.tv_event_name);
tvTime = (TextView) view.findViewById(R.id.tv_event_time);
/* ... */
// Find columns
nameIndex = cursor.getColumnIndexOrThrow("name");
timeIndex = cursor.getColumnIndexOrThrow("time");
}
}
}
AbstractCursorRecyclerAdapter
public abstract class AbstractCursorRecyclerAdapter extends UltimateViewAdapter {
// PATCH: Because RecyclerView.Adapter in its current form doesn't natively support cursors,
// we "wrap" a CursorAdapter that will do all teh job for us
protected final CursorAdapter cursorAdapter;
protected final Context context;
public AbstractCursorRecyclerAdapter(CursorAdapter cursorAdapter, Context context) {
setHasStableIds(true);
this.cursorAdapter = cursorAdapter;
this.context = context;
}
@Override
public int getAdapterItemCount() {
return cursorAdapter.getCount();
}
@Override
public long getItemId(int position) {
return cursorAdapter.getItemId(position);
}
private Model getItem(int position) {
return (Model) cursorAdapter.getItem(position);
}
public CursorAdapter asCursorAdapter() {
return cursorAdapter;
}
public void remove(int position) {
Model entity = getItem(position);
if (entity != null) {
entity.delete();
notifyDataSetChanged();
}
}
}
Gradle dependencies
compile 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
compile 'com.marshalchen.ultimaterecyclerview:library:0.3.2'