I'm trying to display a RecyclerView list fed from a DBFlow (SQLite) backend. Each item in the list represents an item that once selected will transition to a detail activity view. As with any CursorLoader, I'm expecting that each time changes happen in the Loader backing the view, the layout will update itself.
This holds true when testing in Marshmallow but fails to do so in both Lollipop and Jelly Bean (4.3). In case of failure, a single item gets displayed in the list, despite seeing in the debugger that onBindViewHolder() gets called once for each item (with proper data arriving in the cursor, btw).
Following are the main elements involved in my opinion. I deliberately simplified the code, feel free to request more details if deemed necessary:
onResume() override in the activity that displays the list:
@Override
protected void onResume() {
super.onResume();
getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle cursor) {
return BackendManager.getListCursorLoader(mContext);
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
int numItemsRetrieved = cursor.getCount();
if ((numItemsRetrieved == 1) {
Optional<Item> selectedItemOpt = BackendManager.getSingleItem();
if (selectedItemOpt.isPresent()) {
Item selectedItem = selectedItemOpt.get();
Intent t = new Intent(mContext, ItemDetailActivity.class);
Bundle b = new Bundle();
b.putSerializable("item", selectedItem);
it.putExtras(b);
startActivityForResult(it, SHOW_ITEM_DETAIL);
}
}
((ItemListRecyclerAdapter) mRecyclerListView.getAdapter()).swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
((ItemListRecyclerAdapter) mRecyclerListView.getAdapter()).swapCursor(null);
}
});
}
CursorLoader generation within a static manager:
public static CursorLoader getListCursorLoader(Context ctx) {
return new CursorLoader(
ctx, // Context
Item.CONTENT_URI, // Uri
null, // Projection
null, // Selection
null, // selectionArgs
null // sortOrder
);
}
RecyclerView Adapter for Cursors. Currently using skyfish's:
public class ItemListRecyclerAdapter extends CursorRecyclerViewAdapter<ItemListRecyclerAdapter.ViewHolder> {
private Context mContext;
public ItemListRecyclerAdapter(Context context, Cursor cursor) {
super(context, cursor);
mContext = context;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView itemText;
public ImageView itemImage;
public ViewHolder(View view) {
super(view);
itemText = (TextView) view.findViewById(R.id.itemTitle);
itemImage = (ImageView) view.findViewById(R.id.itemPicture);
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.activity_6_list_item, parent, false);
ViewHolder vh = new ViewHolder(itemView);
return vh;
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) {
// Find fields to populate in inflated template
String itemTextData = cursor.getString(cursor.getColumnIndex("itemText"));
viewHolder.itemText.setText(itemTextData);
// Force text fit
AutofitHelper afh = AutofitHelper.create(viewHolder.itemText);
afh.setMaxTextSize(TypedValue.COMPLEX_UNIT_SP, 22.0f);
String itemImageUrl = cursor.getString(cursor.getColumnIndex("logo"));
if ((itemImageUrl != null) && (!itemImageUrl.isEmpty())) {
Picasso.with(mContext)
.load(itemImageUrl)
.resize(300, 300)
.centerCrop()
.placeholder(R.drawable.placeholder)
.into(viewHolder.itemImage);
} else {
viewHolder.itemImage.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.placeholder));
}
}
}
As stated above, this has been tested and verified with:
Am I missing something here? Any hints on where to look further?
Since there haven't been any answers, I'll explain what I ended up doing to work around this issue.
After a full rewrite, I got rid of skyfish's Cursor Adapter and went full throttle with Android's RecyclerView interfaces. Instead of using a Cursor/Adapter scheme to pull data from the SQLite backend and swap cursor when changes where made to the underlying storage, I simply enabled a get-all-items static method in the storage manager to retrieve the latest data. To update the list whenever data changed, I used DBFlow's Observables to get notified of such change. Once the change is detected, I retrieve the updated data with such get-all-items and update the RecyclerView accordingly performing proper notifyItem() calls to animate layout changes.
No need for additional dependencies or code outside Android & DBFlow. This approach has been validated in all the devices described in the original question.