Best practices to use realm with a recycler view?

前端 未结 5 630
再見小時候
再見小時候 2020-12-04 15:54

Do you guys have any best practices regarding using realm with a recyclerview ? I know it\'s generic question but I found nothing on it on the internet. For example I run in

5条回答
  •  有刺的猬
    2020-12-04 16:39

    Now that with Realm 0.88.2 we can make a RecyclerView adapter that updates the RecyclerView with more precision than using notifyDataSetChanged() every time. This can be accomplished by using the new ability create custom methods.

    Overriding the equals method, in the realm object that will be used with the recycler adapter, is all that will be needed. (You don't actually need to override equals... but you may find that realm objects do not equal each other when you expect them to. This will lead to unnecessary recyclerview updates after running diff)

    Then add Google's java-diff-utils to your gradle dependencies

        compile 'com.googlecode.java-diff-utils:diffutils:1.3.0'
    

    Using this RealmRecyclerViewAdapter implementation a copy of realmResults is made at start, and on every change to compare against future changes. Detected changes are used to update the RecyclerView as appropriate

    public abstract class RealmRecyclerViewAdapter
        extends RecyclerView.Adapter {
    
    
    protected RealmResults realmResults;
    protected List lastCopyOfRealmResults;
    int maxDepth = 0;
    
    private RealmChangeListener realmResultsListener;
    Realm realm;
    
    public RealmRecyclerViewAdapter(RealmResults realmResults, boolean automaticUpdate) {
        this(realmResults, automaticUpdate, 0);
    }
    
    /**
     *
     * @param realmResults
     * @param automaticUpdate
     * @param maxDepth limit of the deep copy when copying realmResults. All references after this depth will be {@code null}. Starting depth is {@code 0}.
     *                 A copy of realmResults is made at start, and on every change to compare against future changes. Detected changes are used to update
     *                 the RecyclerView as appropriate
     */
    public RealmRecyclerViewAdapter(RealmResults realmResults, boolean automaticUpdate, int maxDepth) {
    
        this.realmResultsListener = (!automaticUpdate) ? null : getRealmResultsChangeListener();
    
        if (realmResultsListener != null && realmResults != null) {
            realmResults.addChangeListener(realmResultsListener);
        }
        this.realmResults = realmResults;
        realm = Realm.getDefaultInstance();
        this.maxDepth = maxDepth;
    
        lastCopyOfRealmResults = realm.copyFromRealm(realmResults, this.maxDepth);
    }
    
    
    
    @Override
    public int getItemCount() {
        return realmResults != null ? realmResults.size() : 0;
    }
    
    /**
     * Make sure this is called before a view is destroyed to avoid memory leaks do to the listeners.
     * Do this by calling setAdapter(null) on your RecyclerView
     * @param recyclerView
     */
    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);
        if (realmResultsListener != null) {
            if (realmResults != null) {
                realmResults.removeChangeListener(realmResultsListener);
            }
        }
        realm.close();
    }
    
    /**
     * Update the RealmResults associated with the Adapter. Useful when the query has been changed.
     * If the query does not change you might consider using the automaticUpdate feature.
     *
     * @param queryResults the new RealmResults coming from the new query.
     * @param maxDepth limit of the deep copy when copying realmResults. All references after this depth will be {@code null}. Starting depth is {@code 0}.
     *                 A copy of realmResults is made at start, and on every change to compare against future changes. Detected changes are used to update
     *                 the RecyclerView as appropriate
     */
    public void updateRealmResults(RealmResults queryResults, int maxDepth) {
        if (realmResultsListener != null) {
            if (realmResults != null) {
                realmResults.removeChangeListener(realmResultsListener);
            }
        }
    
        realmResults = queryResults;
        if (realmResults != null && realmResultsListener !=null) {
            realmResults.addChangeListener(realmResultsListener);
        }
        this.maxDepth = maxDepth;
        lastCopyOfRealmResults = realm.copyFromRealm(realmResults,this.maxDepth);
    
        notifyDataSetChanged();
    }
    
    public T getItem(int position) {
        return realmResults.get(position);
    }
    
    public int getRealmResultsSize(){
        return realmResults.size();
    }
    
    
    private RealmChangeListener getRealmResultsChangeListener() {
        return new RealmChangeListener>() {
            @Override
            public void onChange(RealmResults element) {
                if (lastCopyOfRealmResults != null && !lastCopyOfRealmResults.isEmpty()) {
                    if (realmResults.isEmpty()) {
                        // If the list is now empty, just notify the recyclerView of the change.
                        lastCopyOfRealmResults = realm.copyFromRealm(realmResults,maxDepth);
                        notifyDataSetChanged();
                        return;
                    }
                    Patch patch = DiffUtils.diff(lastCopyOfRealmResults, realmResults);
                    List deltas = patch.getDeltas();
                    lastCopyOfRealmResults = realm.copyFromRealm(realmResults,maxDepth);
                    if (!deltas.isEmpty()) {
                        List deleteDeltas = new ArrayList<>();
                        List insertDeltas = new ArrayList<>();
                        for (final Delta delta : deltas) {
                            switch (delta.getType()){
                                case DELETE:
                                    deleteDeltas.add(delta);
                                    break;
                                case INSERT:
                                    insertDeltas.add(delta);
                                    break;
                                case CHANGE:
                                    notifyItemRangeChanged(
                                            delta.getRevised().getPosition(),
                                            delta.getRevised().size());
                                    break;
                            }
                        }
                        for (final Delta delta : deleteDeltas) {
                            notifyItemRangeRemoved(
                                    delta.getOriginal().getPosition(),
                                    delta.getOriginal().size());
                        }
                        //item's should be removed before insertions are performed
                        for (final Delta delta : insertDeltas) {
                            notifyItemRangeInserted(
                                    delta.getRevised().getPosition(),
                                    delta.getRevised().size());
                        }
                    }
                } else {
                    notifyDataSetChanged();
                    lastCopyOfRealmResults = realm.copyFromRealm(realmResults,maxDepth);
                }
            }
        };
    }
    
    }
    

提交回复
热议问题