Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RecyclerView sample #5

Open
cmelchior opened this issue May 4, 2016 · 7 comments
Open

RecyclerView sample #5

cmelchior opened this issue May 4, 2016 · 7 comments
Labels

Comments

@cmelchior
Copy link
Contributor

We are seeing a number of support issue around RecyclerView and how to handler changelisteners and Realm updates in that case. See e.g realm/realm-java#2408 and realm/realm-java#2650

We should provide a sample using the RecyclerView that could illustrate how to make this work.

@cmelchior cmelchior added First-Good-Issue First Good Issue - Relatively easy issue for new contributers S:P2 Backlog T-Doc labels May 4, 2016
@Zhuinden
Copy link

Zhuinden commented May 4, 2016

I haven't tested this yet, but based on previous incarnations, this sounds about right.

public abstract class RealmRecyclerViewAdapter<T extends RealmObject, VH extends RecyclerView.ViewHolder>
    extends RecyclerView.Adapter<VH> { //put this in `io.realm`

    protected LayoutInflater inflater;
    protected OrderedRealmCollection<T> adapterData;
    protected Context context;
    private final RealmChangeListener listener;

    public RealmRecyclerViewAdapter(Context context, OrderedRealmCollection<T> data) {
        if (context == null) {
            throw new IllegalArgumentException("Context cannot be null");
        }
        this.context = context;
        this.adapterData = data;
        this.inflater = LayoutInflater.from(context);
        this.listener = new RealmChangeListener<RealmResults<T>>() {
            @Override
            public void onChange(RealmResults<T> results) {
                notifyDataSetChanged();
            }
        };

        if (data != null) {
            addListener(data);
        }
    }

    private void addListener(OrderedRealmCollection<T> data) {
        if (data instanceof RealmResults) {
            RealmResults realmResults = (RealmResults) data;
            realmResults.addChangeListener(listener);
        } else if (data instanceof RealmList) {
            RealmList realmList = (RealmList) data;
            realmList.realm.handlerController.addChangeListenerAsWeakReference(listener);
        } else {
            throw new IllegalArgumentException("RealmCollection not supported: " + data.getClass());
        }
    }

    private void removeListener(OrderedRealmCollection<T> data) {
        if (data instanceof RealmResults) {
            RealmResults realmResults = (RealmResults) data;
            realmResults.removeChangeListener(listener);
        } else if (data instanceof RealmList) {
            RealmList realmList = (RealmList) data;
            realmList.realm.handlerController.removeWeakChangeListener(listener);
        } else {
            throw new IllegalArgumentException("RealmCollection not supported: " + data.getClass());
        }
    }

    /**
     * Returns how many items are in the data set.
     *
     * @return the number of items.
     */
    @Override
    public int getItemCount() {
        if (adapterData == null) {
            return 0;
        }
        return adapterData.size();
    }

    /**
     * Get the data item associated with the specified position in the data set.
     *
     * @param position Position of the item whose data we want within the adapter's
     * data set.
     * @return The data at the specified position.
     */
    public T getItem(int position) {
        if (adapterData == null) {
            return null;
        }
        return adapterData.get(position);
    }

    /**
     * Get the row id associated with the specified position in the list. Note that item IDs are not stable so you
     * cannot rely on the item ID being the same after {@link #notifyDataSetChanged()} or
     * {@link #updateData(OrderedRealmCollection)} has been called.
     *
     * @param position The position of the item within the adapter's data set whose row id we want.
     * @return The id of the item at the specified position.
     */
    @Override
    public long getItemId(int position) {
        // TODO: find better solution once we have unique IDs
        return position;
    }

    /**
     * Updates the data associated with the Adapter.
     *
     * Note that RealmResults and RealmLists are "live" views, so they will automatically be updated to reflect the
     * latest changes. This will also trigger {@code notifyDataSetChanged()} to be called on the adapter.
     *
     * This method is therefore only useful if you want to display data based on a new query without replacing the
     * adapter.
     *
     * @param data the new {@link OrderedRealmCollection} to display.
     */
    public void updateData(OrderedRealmCollection<T> data) {
        if (listener != null) {
            if (adapterData != null) {
                removeListener(adapterData);
            }
            if (data != null) {
                addListener(data);
            }
        }

        this.adapterData = data;
        notifyDataSetChanged();
    }
}

@cmelchior
Copy link
Contributor Author

That is probably a good starting ground, but I am not sure it captures the complexity of some of the use cases we are seeing (e.g drag-n-drop sorting).

@Zhuinden
Copy link

Zhuinden commented May 4, 2016

That's true, and I think that requires stable IDs, and on top of that notifyDatasetChanged() also means no predictive animations on insertion/removal...

I'm pretty sure that when we needed drag-and-drop sorting, we had detached copies (or at least a list that was not a RealmResults<T>), and we also had our own field called rank. I'm not sure this is a problem that can be handled generally, but I could be wrong.

@JM-Agrimap
Copy link

@cmelchior
Copy link
Contributor Author

Thorben has done some great work with his RecyclerView, but his library is more like a self-contained UI widget. What we want to do with the classes in this repo is making sure they can be used for building your own custom widget. For that reason his version fits better in its own repository.

@cmelchior cmelchior self-assigned this May 13, 2016
@cmelchior cmelchior added S:In Progress T-Doc and removed First-Good-Issue First Good Issue - Relatively easy issue for new contributers S:P2 Backlog T-Doc labels May 13, 2016
@cmelchior cmelchior removed their assignment May 13, 2016
@cmelchior
Copy link
Contributor Author

Partly yes, but I believe this issue was also created to address the problems described here realm/realm-java#4245 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants