将领域与回收者视图一起使用的最佳实践?

Posted

技术标签:

【中文标题】将领域与回收者视图一起使用的最佳实践?【英文标题】:Best practices to use realm with a recycler view? 【发布时间】:2015-05-13 18:14:06 【问题描述】:

你们有关于使用领域和 recyclerview 的最佳实践吗? 我知道这是一个通用问题,但我在互联网上什么也没找到。例如,我在尝试对一行进行简单的颜色更改时遇到了很多麻烦。例如考虑这个典型的用法:

public class User extends RealmObject 
   @PrimaryKey
   String name;

   boolean isSelected;
   ... 

   constructor, getter and setters 


public class UserAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> 

    private RealmResults<User> users;

    public UserAdapter(RealmResults<User> users) 
        this.users = users;
    

   ...

   public void markAsSelected(int position)
      // get the old selected user and deselect it
      notifyItemChanged(? how do i get the position given my User has no index ?);

      // mark as selected the new user at position
   

我遇到了很多问题,因为我在互联网上找不到任何东西。我知道这是因为我不知道如何正确使用领域。但找到正确的道路本身就是一场斗争。我阅读了他们所有的文档,但无济于事。

编辑:因为我被要求 --> 与其说“我有一堆关于 [that] 的问题”,不如描述您的问题,我们将尝试为您的不理解提供见解和答案。

所以我的问题很简单:

我有一个 RealmUser :

public class RealmUser extends RealmObject 

    @PrimaryKey
    private String key;

    private String name;
    private boolean isSelected;
    private boolean editMode;
    private RealmList<RealmItemList> lists;


    public RealmUser() 

    public RealmUser(String name, RealmList<RealmItemList> lists, boolean isSelected , boolean editMode) 
        this.key = UUID.randomUUID().toString();
        this.name = name;
        this.isSelected = isSelected;
        this.editMode = editMode;
        if (lists ==null)
            this.lists = new RealmList<RealmItemList>();
        else
            this.lists = lists;
        
    

    public String getKey() 
        return key;
    

    public void setKey(String key) 
        this.key = key;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public boolean isSelected() 
        return isSelected;
    

    public void setSelected(boolean isSelected) 
        this.isSelected = isSelected;
    

    public boolean isEditMode() 
        return editMode;
    

    public void setEditMode(boolean editMode) 
        this.editMode = editMode;
    

    public RealmList<RealmItemList> getLists() 
        return lists;
    

    public void setLists(RealmList<RealmItemList> lists) 
        this.lists = lists;
    



我使用以下方法将其放入 RealmResults 数组中:

RealmResults users = realm.where(RealmUser.class).findAll();

我将我的用户数组传递给我的自定义用户适配器:

public class UserAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> 

    private RealmResults<RealmUser> users;

    public UserAdapter(RealmResults<RealmUser> users) 
        this.users = users;
    

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 

        LayoutInflater inflater = LayoutInflater.from(parent.getContext());

        if(viewType == 1)
            View v = inflater.inflate(R.layout.detail_user, parent, false);
            return new UserHolder(v);
        else if(viewType == 2)
            View v = inflater.inflate(R.layout.edit_user, parent, false);
            return new editUserHolder(v);
        else 
            return null;
        
    

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) 

        RealmUser user = users.get(position);
        String userName = user.getName();
        boolean isSelected = user.isSelected();

        if (holder instanceof UserHolder )
            UserHolder uHolder = (UserHolder) holder;
            uHolder.userText.setText(userName);
            if (isSelected)
                uHolder.userContainer.setBackgroundColor(Color.parseColor("#607D8B"));
            
        else if(holder instanceof editUserHolder)
            editUserHolder eUserHolder = (editUserHolder) holder;
            eUserHolder.userEditContainer.setBackgroundColor(Color.parseColor("#eeeeee"));
        



    

    @Override
    public int getItemViewType(int position) 
        RealmUser user = users.get(position);

        if (user.isEditMode())
            return 2;
        else 
            return 1;
        

    

    @Override
    public int getItemCount() 
        return users.size();
    

    public void markAsSelected(int position, DrawerLayout mDrawerLayout , Toolbar toolbar, Realm realm)
        // Here is my problem : How do I get the already selected user asuming there is one in my db and notify the UI that I changed that item. 


有一个自定义的点击监听器:获取recyclerview被点击的项目:

public class UserClickListener implements RecyclerView.OnItemTouchListener

    public static interface OnItemClickListener
        public void onItemClick(View v, int position);
    

    private OnItemClickListener mListener;
    private GestureDetector mGestureDetector;


    public UserClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener)
    
        mListener = listener;

        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() 

            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) 
                View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
                if(childView != null && mListener != null)
                
                    mListener.onItemClick(childView, recyclerView.getChildPosition(childView));
                    return true;
                
                return false;
            



        );

    


    @Override
    public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) 
        View childView = view.findChildViewUnder(e.getX(), e.getY());

        if(childView != null && mListener != null && mGestureDetector.onTouchEvent(e))
        
            mListener.onItemClick(childView, view.getChildPosition(childView));
        

        return false;
    

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) 

    

我使用 addOnItemTouchListener 添加到我的 recyclerView 中:

mListRecycler.addOnItemTouchListener(new UserClickListener(getActivity(), mListRecycler, new UserClickListener.OnItemClickListener()

            @Override
            public void onItemClick(View view, int position)
            
                UserAdapter myadapter = (UserAdapter) mListRecycler.getAdapter();
                myadapter.markAsSelected(position, mDrawerLayout , mToolbar, realm);
            
    ));

【问题讨论】:

欢迎来到 Stack Overflow!请重新阅读问题,使其更清晰并删除错别字。另外,我们喜欢具体的问题。不要说“我对 [that] 有很多问题”,而是描述您的问题,我们将尝试为您的不理解提供见解和答案。 好的,我会编辑我的帖子。 【参考方案1】:

0.89.0 及更高版本的答案

对于最新版本,您应该使用RealmRecyclerViewAdapter in the realm-android-adapters repository.

版本:

使用 1.5.0 到 2.X

使用 2.1.1 到 4.X

5.X以上使用3.0.0


旧版本的旧答案:

我根据RealmBaseAdapter的实现做了这个RealmRecyclerViewAdapter

这适用于 v0.89.0 及更高版本

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();
    


这适用于 v0.84.0 及更高版本,但早于 v0.89.0(针对 v0.87.5 更新):

public abstract class RealmRecyclerViewAdapter<T extends RealmObject, VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<VH>  //put this in `io.realm`
    protected LayoutInflater inflater;
    protected RealmResults<T> realmResults;
    protected Context context;
    private final RealmChangeListener listener;

    public RealmRecyclerViewAdapter(Context context, RealmResults<T> realmResults, boolean automaticUpdate) 
        if (context == null) 
            throw new IllegalArgumentException("Context cannot be null");
        
        this.context = context;
        this.realmResults = realmResults;
        this.inflater = LayoutInflater.from(context);
        this.listener = (!automaticUpdate) ? null : new RealmChangeListener() 
            @Override
            public void onChange() 
                notifyDataSetChanged();
            
        ;

        if (listener != null && realmResults != null) 
            realmResults.realm.handlerController.addChangeListenerAsWeakReference(listener);
        
    

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

    /**
     * Returns the item associated with the specified position.
     *
     * @param i index of item whose data we want.
     * @return the item at the specified position.
     */
    public T getItem(int i) 
        if (realmResults == null) 
            return null;
        
        return realmResults.get(i);
    

    /**
     * Returns the current ID for an item. Note that item IDs are not stable so you cannot rely on the item ID being the
     * same after @link #notifyDataSetChanged() or @link #updateRealmResults(RealmResults) has been called.
     *
     * @param i index of item in the adapter.
     * @return current item ID.
     */
    @Override
    public long getItemId(int i) 
        // TODO: find better solution once we have unique IDs
        return i;
    

    /**
     * Updates the RealmResults associated to 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.
     */
    public void updateRealmResults(RealmResults<T> queryResults) 
        if (listener != null) 
            // Making sure that Adapter is refreshed correctly if new RealmResults come from another Realm
            if (this.realmResults != null) 
                this.realmResults.realm.removeChangeListener(listener);
            
            if (queryResults != null) 
                queryResults.realm.addChangeListener(listener);
            
        

        this.realmResults = queryResults;
        notifyDataSetChanged();
    

    public void addChangeListenerAsWeakReference(RealmChangeListener realmChangeListener) 
        if(realmResults != null) 
            realmResults.realm.handlerController.addChangeListenerAsWeakReference(realmChangeListener);
        
    


这适用于OLDER THAN 0.84.0

public abstract class RealmRecyclerViewAdapter<T extends RealmObject, VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<VH>  //put this in `io.realm`
    protected LayoutInflater inflater;
    protected RealmResults<T> realmResults;
    protected Context context;

    private final RealmChangeListener listener;

    public RealmRecyclerViewAdapter(Context context, RealmResults<T> realmResults, boolean automaticUpdate) 
        if(context == null) 
            throw new IllegalArgumentException("Context cannot be null");
        
        this.context = context;
        this.realmResults = realmResults;
        this.inflater = LayoutInflater.from(context);
        this.listener = (!automaticUpdate) ? null : new RealmChangeListener() 
            @Override
            public void onChange() 
                notifyDataSetChanged();
            
        ;

        if(listener != null && realmResults != null) 
            realmResults.getRealm()
                    .addChangeListener(listener);
        
    

    @Override
    public long getItemId(int i) 
        // TODO: find better solution once we have unique IDs
        return i;
    

    public T getItem(int i) 
        if(realmResults == null) 
            return null;
        
        return realmResults.get(i);
    

    public void updateRealmResults(RealmResults<T> queryResults) 
        if(listener != null) 
            // Making sure that Adapter is refreshed correctly if new RealmResults come from another Realm
            if(this.realmResults != null) 
                realmResults.getRealm().removeChangeListener(listener);
            
            if(queryResults != null) 
                queryResults.getRealm().addChangeListener(listener);
            
        

        this.realmResults = queryResults;
        notifyDataSetChanged();
    

    @Override
    public int getItemCount() 
        if(realmResults == null) 
            return 0;
        
        return realmResults.size();
    

【讨论】:

getRealm() 受包保护的任何具体原因? @VaibhavGupta 不知道,尽管有人告诉我,如果你将你的类命名为 io.realm 包中,那么你根本不需要反射。这是package,不是private【参考方案2】:

上面的一些答案包括反射,更不用说分段的 RecyclerView 会导致并发症。它们也不支持添加和删除项目。这是我的 RecyclerView 适配器版本,可与 Realm 一起使用,支持分段 RecyclerView,如果需要,还可以在任意位置添加和删除项目

这是我们的 AbstractRealmAdapter,它处理所有低级的东西,显示页眉、页脚、项目、在 RealmResults 中加载数据、管理项目类型

import io.realm.Realm;
import io.realm.RealmObject;
import io.realm.RealmResults;

public abstract class AbstractRealmAdapter<T extends RealmObject, VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<VH> 

    public static final int HEADER_COUNT = 1;
    public static final int FOOTER_COUNT = 1;

    //Our data source
    protected RealmResults<T> mResults;

    public AbstractRealmAdapter(Realm realm) 
        //load data from subclasses
        mResults = loadData(realm);
        notifyDataSetChanged();
    


    public int getHeaderCount() 
        return hasHeader() ? HEADER_COUNT : 0;
    

    public int getFooterCount() 
        return hasFooter() ? FOOTER_COUNT : 0;
    

    public boolean isHeader(int position) 
        if (hasHeader()) 
            return position < HEADER_COUNT;
         else 
            return false;
        
    

    public boolean isFooter(int position) 
        if (hasFooter()) 
            return position >= getCount() + getHeaderCount();
         else 
            return false;
        
    

    @Override
    public long getItemId(int i) 
        return i;
    


    @Override
    public final int getItemViewType(int position) 
        if (isHeader(position)) 
            return ItemType.HEADER.ordinal();
         else if (isFooter(position)) 
            return ItemType.FOOTER.ordinal();
         else 
            return ItemType.ITEM.ordinal();
        
    

    /**
     * @param position the position within our adapter inclusive of headers,items and footers
     * @return an item only if it is not a header or a footer, otherwise returns null
     */
    public T getItem(int position) 
        if (!isHeader(position) && !isFooter(position) && !mResults.isEmpty()) 
            return mResults.get(position - getHeaderCount());
        
        return null;
    


    @Override
    public final int getItemCount() 
        return getHeaderCount() + getCount() + getFooterCount();
    

    public final int getCount() 
        return mResults.size();
    

    public abstract boolean hasHeader();

    public abstract boolean hasFooter();


    public void setData(RealmResults<T> results) 
        mResults = results;
        notifyDataSetChanged();
    

    protected abstract RealmResults<T> loadData(Realm realm);

    public enum ItemType 
        HEADER, ITEM, FOOTER;
    

要通过某种方法添加项目或通过滑动删除项目,我们有一个 AbstractMutableRealmAdapter 形式的扩展,如下所示

import android.support.v7.widget.RecyclerView;

import io.realm.Realm;
import io.realm.RealmObject;

public abstract class AbstractMutableRealmAdapter<T extends RealmObject, VH extends RecyclerView.ViewHolder>
        extends AbstractRealmAdapter<T, VH> implements OnSwipeListener 

    private Realm realm;

    public AbstractMutableRealmAdapter(Realm realm) 
        //call the superclass constructor to load data from subclasses into realmresults
        super(realm);
        this.realm = realm;
    

    public void add(T item, boolean update) 
        realm.beginTransaction();
        T phraseToWrite = (update == true) ? realm.copyToRealmOrUpdate(item) : realm.copyToRealm(item);
        realm.commitTransaction();
        notifyItemRangeChanged(0, mResults.size());
    

    @Override
    public final void onSwipe(int position) 
        if (!isHeader(position) && !isFooter(position) && !mResults.isEmpty()) 
            int itemPosition = position - getHeaderCount();
            realm.beginTransaction();
            T item = mResults.get(itemPosition);
            item.removeFromRealm();
            realm.commitTransaction();
            notifyItemRemoved(position);
        
    


注意接口 OnSwipeListener 的使用,看起来像这样

public interface OnSwipeListener 
    /**
     * @param position the position of the item that was swiped within the RecyclerView
     */
    void onSwipe(int position);

这个 SwipeListener 用于在 TouchHelperCallback 中执行 Swipe 以删除,这反过来又用于直接从 Realm 中删除对象,如下所示

import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;

public class TouchHelperCallback extends ItemTouchHelper.Callback 

    private final OnSwipeListener mSwipeListener;

    public TouchHelperCallback(OnSwipeListener adapter) 
        mSwipeListener = adapter;
    

    /**
     * @return false if you dont want to enable drag else return true
     */
    @Override
    public boolean isLongPressDragEnabled() 
        return false;
    

    /**
     * @return true of you want to enable swipe in your RecyclerView else return false
     */
    @Override
    public boolean isItemViewSwipeEnabled() 
        return true;
    

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 
        //We want to let the person swipe to the right on devices that run LTR and let the person swipe from right to left on devices that run RTL
        int swipeFlags = ItemTouchHelper.END;
        return makeMovementFlags(0, swipeFlags);
    

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                          RecyclerView.ViewHolder target) 
        return false;
    

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) 
        mSwipeListener.onSwipe(viewHolder.getAdapterPosition());
    

完整的实施演示可在此处查看https://github.com/slidenerd/SpamWordList/tree/spamphraser_with_realmresults_base随时提出任何改进建议

我用 notifyDataSetChanged 替换了 notifyXXX 方法,RealmResults 对象是活动对象,这意味着它们会在数据更新时自动更改,我尝试调用 notifyXXX 方法并导致 RecyclerView 不一致异常,我很清楚 notifyDataSetChanged( ) 会弄乱动画,让你们了解克服不一致错误的解决方案,同时提供良好的适配器体验

【讨论】:

晚会很晚,但演示已移动:https://github.com/canadianeagle/SpamWordList【参考方案3】:

现在有了 Realm 0.88.2,我们可以制作一个 RecyclerView 适配器,它比每次都使用 notifyDataSetChanged() 更精确地更新 RecyclerView。这可以通过使用新功能创建自定义方法来完成。

在将与回收器适配器一起使用的领域对象中重写 equals 方法,这就是所需要的。 (您实际上需要覆盖equals...但是您可能会发现领域对象并不如您所愿。这将导致运行diff后不必要的recyclerview更新)

然后将 Google 的 java-diff-utils 添加到您的 gradle 依赖项中

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

使用此 RealmRecyclerViewAdapter 实现,会在开始时生成 realmResults 的副本,并在每次更改时与将来的更改进行比较。检测到的更改用于根据需要更新 RecyclerView

public abstract class RealmRecyclerViewAdapter<T extends RealmObject, VH extends RecyclerView.ViewHolder>
    extends RecyclerView.Adapter<VH> 


protected RealmResults<T> realmResults;
protected List<T> lastCopyOfRealmResults;
int maxDepth = 0;

private RealmChangeListener realmResultsListener;
Realm realm;

public RealmRecyclerViewAdapter(RealmResults<T> 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<T> 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<T> 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<RealmResults<T>>() 
        @Override
        public void onChange(RealmResults<T> 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<Delta> deltas = patch.getDeltas();
                lastCopyOfRealmResults = realm.copyFromRealm(realmResults,maxDepth);
                if (!deltas.isEmpty()) 
                    List<Delta> deleteDeltas = new ArrayList<>();
                    List<Delta> 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);
            
        
    ;



【讨论】:

这解决了在另一个线程上更改领域的情况下领域的一个非常大的问题。解决的问题是“对象无效,是否被另一个线程删除”错误,差异库很好地解决了问题【参考方案4】:

您的帖子甚至没有包含真正的问题。

你看过这篇文章了吗:http://gradlewhy.ghost.io/realm-results-with-recyclerview/ ?

不知道为什么不只是在适配器中使用 ArrayList 并将 RealmResult 中的所有元素添加到该列表中。谁能解释为什么博文中的解决方案会更好?

【讨论】:

感谢您的博文。 why you wouldn't just use an ArrayList in your adapter and add all elements from the RealmResult to that list 因为那太慢了,而且丢掉了 REALM 的全部意义! (关键是查询是惰性的,这意味着 RealmQuery 仅在需要时执行,并逐个获取元素。之所以有用是因为您没有获得所有 20000 个结果一次查询)【参考方案5】:

从Thorben Primke 实现Realm Add-on 是一种非常方便的方法 用于使用 Realm 数据库处理 Recycler View 应用程序。他的github 有很好的例子来说明它可以实现的方式。

我将在此处包括我的,以便您举个例子。首先为 jitpack.io 修改你的项目构建 gradle:

allprojects 
repositories 
    jcenter()
    maven  url "https://jitpack.io" 

然后你的模块 gradle 指向库:(注意,检查最新版本)

dependencies 
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.github.thorbenprimke:realm-recyclerview:0.9.20'

使用 RealmRecyclerView 为回收站视图创建 xml 布局:

    <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"

    android:id="@+id/swipeRefreshLayout"
    android:layout_
    android:layout_>

    <co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView
        android:id="@+id/realm_recycle_view"
        android:layout_
        android:layout_
        app:rrvIsRefreshable="true"
        app:rrvEmptyLayoutId="@layout/empty_view"
        app:rrvLayoutType="LinearLayout"
        app:rrvSwipeToDelete="true"
        />
</RelativeLayout>

现在在您的 RealmRecycler Fragment 中获取 RealmObjects 的 Realm 查询结果,膨胀并定义 Primke RealmAdapter:

 Log.i(TAG, " Obtain Filtered List");
    final RealmResults <Session> realmResults = queryD.findAllSorted(
            "sessionId", Sort.DESCENDING);

    Log.i(TAG, " Inflate realm List");
    View view = inflater.inflate(R.layout.realm_card_recycler2, null);

    Log.i(TAG, " Define and configure SessionRealmAdapter");
    SessionRealmAdapter sessionRealmAdapter =
            new SessionRealmAdapter(getActivity(), realmResults, true,    true);`enter code here`
    RealmRecyclerView realmRecyclerView =
            (RealmRecyclerView)  view.findViewById(R.id.realm_recycle_view);
    realmRecyclerView.setAdapter(sessionRealmAdapter);

最后为你想要的动作配置领域适配器。我有几个点击并打开滑动删除以删除领域记录。

      public class SessionRealmAdapter
          extends RealmBasedRecyclerViewAdapter<Session, SessionRealmAdapter.ViewHolder> 

      public class ViewHolder extends RealmViewHolder 

          public TextView sessionTextView;
          public ViewHolder(FrameLayout container) 
              super(container);
              this.sessionTextView = (TextView) container.findViewById(R.id.session_text_view);
          
      

      public SessionRealmAdapter(
              Context context,
              RealmResults<Session> realmResults,
              boolean automaticUpdate,
              boolean animateResults) 
          super(context, realmResults, automaticUpdate, animateResults);
      

      @Override
      public ViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int viewType) 
          View v = inflater.inflate(R.layout.session_simple_view, viewGroup, false);
          return new ViewHolder((FrameLayout) v);
      

      @Override
      public void onBindRealmViewHolder(ViewHolder viewHolder, int position) 
          final Session singleSession = realmResults.get(position);
          viewHolder.sessionTextView.setText(singleSession.gettMethod());
          viewHolder.sessionTextView.setOnClickListener(
                  new View.OnClickListener()

                      @Override
                      public void onClick(View v)
                          selectSession(singleSession);
                          showMessage(" Selected "+singleSession.gettMethod());
                      
                  
          );
          viewHolder.sessionTextView.setOnLongClickListener(
                  new View.OnLongClickListener()

                      @Override
                      public boolean onLongClick(View v)
                            showInformationDialog(singleSession);
                            showMessage("Long click selected for "
                                    +singleSession.getSessionTitle());
                            return true;
                      
                  
          );
      
  

【讨论】:

以上是关于将领域与回收者视图一起使用的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章

将 HTML 与基于 XML 的语言(如 SVG)一起使用的最佳实践是啥?

将 Querydsl 与 Spring Data 一起使用时的最佳实践

最佳实践:处理来自表格视图单元格内按钮的操作?

通过视图模型接收 DateTime 输入的最佳实践是啥?

AFNetworking 最佳实践将逻辑与视图控制器和职责分离以显示数据

Jenkins管道最佳实践Top 10