How to update RecyclerView on dataset changes?

怎甘沉沦 提交于 2019-12-14 02:51:24

问题


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'

来源:https://stackoverflow.com/questions/31294692/how-to-update-recyclerview-on-dataset-changes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!