Лучшие практики использования области с представлением переработчика?

Есть ли у вас какие-либо рекомендации по использованию области с 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
   }

У меня было много проблем, так как я ничего не мог найти в Интернете. Я знаю, это потому, что я не знаю, как правильно использовать область. Но поиск правильного пути сам по себе является борьбой. Я прочитал всю их документацию, но безрезультатно.

РЕДАКТИРОВАТЬ: Поскольку меня попросили --> Вместо того, чтобы говорить: «У меня куча проблем с [этим]», опишите свою проблему (проблемы), и мы постараемся предоставить информацию и ответы на ваши непонимания.

Итак, моя проблема проста:

У меня есть 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) {

    }
}

Который я добавляю в свой recyclerView с помощью addOnItemTouchListener :

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

person Anghelut Florin    schedule 11.03.2015    source источник
comment
Добро пожаловать в Stack Overflow! Пожалуйста, перечитайте вопрос, чтобы сделать его более ясным и удалить опечатки. Кроме того, нам нравятся конкретные темы. Вместо того, чтобы говорить, что у меня куча проблем с [этим], опишите свою проблему (проблемы), и мы постараемся предоставить информацию и ответы на ваши непонятки.   -  person Kyll    schedule 11.03.2015
comment
Хорошо, я отредактирую свой пост.   -  person Anghelut Florin    schedule 11.03.2015


Ответы (5)


ОТВЕТ ДЛЯ 0.89.0 И ВЫШЕ

Для последних версий следует использовать RealmRecyclerViewAdapter в репозитории realm-android-adapters.

Версии:

  • Используйте 1.5.0 до 2.X

  • Используйте 2.1.1 до 4.X

  • Используйте 3.0.0 выше 5.X


СТАРЫЙ ОТВЕТ ДЛЯ СТАРЫХ ВЕРСИЙ:

Я сделал это RealmRecyclerViewAdapter на основе реализации RealmBaseAdapter.

Это для 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);
        }
    }
}

Это для РАНЕЕ 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();
    }
}
person EpicPandaForce    schedule 02.07.2015
comment
Любая конкретная причина, по которой getRealm() защищен пакетом? - person Vaibhav Gupta; 28.08.2015
comment
@VaibhavGupta понятия не имею, хотя мне сказали, что если вы назовете свой класс в пакете io.realm, вам вообще не понадобится отражение. Это package, а не private! - person EpicPandaForce; 28.08.2015

Некоторые из приведенных выше ответов включают отражение, не говоря уже о том, что секционированный 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 используется для выполнения Swipe для удаления внутри нашего TouchHelperCallback, который, в свою очередь, используется для непосредственного удаления объектов из 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. Не стесняйтесь предлагать любые улучшения

Я заменил методы notifyXXX на notifyDataSetChanged, объекты RealmResults являются живыми объектами, что означает, что они автоматически изменяются при обновлении данных, я попытался вызвать методы notifyXXX, и они вызвали исключение несогласованности RecyclerView, я хорошо знаю тот факт, что notifyDataSetChanged() приведет к путанице с анимацией, будет держать вас в курсе решения, которое устраняет ошибку несоответствия и в то же время обеспечивает хорошее взаимодействие с адаптером.

person PirateApp    schedule 24.09.2015
comment
ОЧЕНЬ поздно на вечеринку, но демо переехало: https://github.com/canadianeagle/SpamWordList - person Carl Smith; 26.01.2021

Теперь, когда в Realm 0.88.2 мы можем создать адаптер RecyclerView, который обновляет RecyclerView с большей точностью, чем каждый раз используя notifyDataSetChanged(). Этого можно добиться с помощью новой возможности создания пользовательских методов.

Все, что потребуется, — переопределить метод equals в объекте области, который будет использоваться с адаптером ресайклера. (На самом деле вам не нужно переопределять equals... но вы можете обнаружить, что объекты области не равны друг другу, когда вы этого ожидаете. Это приведет к ненужным обновлениям recyclerview после запуска diff)

Затем добавьте 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);
            }
        }
    };
}

}
person luca992    schedule 17.03.2016
comment
Это решает очень большую проблему с областью в ситуации, когда изменения области выполняются в другом потоке. Решенная проблема будет заключаться в том, что объект недействителен, был ли он удален из-за ошибки другого потока, с разницей в библиотеке, эта проблема решена красиво - person TheAnimatrix; 25.01.2017

В вашем посте нет даже настоящего вопроса.

Вы проверили этот пост: http://gradlewhy.ghost.io/realm-results-with-recyclerview/ ?

Не уверен, почему бы вам просто не использовать ArrayList в своем адаптере и не добавить все элементы из RealmResult в этот список. Может ли кто-нибудь объяснить, почему решение в сообщении в блоге было бы лучше?

person lukasvo    schedule 08.04.2015
comment
Спасибо за сообщение в блоге. - person user1480139; 23.07.2015
comment
why you wouldn't just use an ArrayList in your adapter and add all elements from the RealmResult to that list Потому что это МЕДЛЕННО и отбрасывает весь смысл REALM! - person EpicPandaForce; 08.08.2015
comment
(дело в том, что запросы ленивы, то есть RealmQuery выполняется только тогда, когда это необходимо, и получает элементы один за другим. Причина, по которой это полезно, заключается в том, что вы не получаете все 20000 результатов для вашего запроса одновременно) - person EpicPandaForce; 08.08.2015

Внедрение надстройки Realm от Thorben Primke — это очень удобный метод для работы с приложениями Recycler View с базами данных Realm. Его github содержит хорошие примеры того, как это можно реализовать.

Я включу сюда свой, чтобы у вас был пример. Сначала измените градиент сборки проекта для jitpack.io:

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'

Создайте макет xml для представления ресайклера, используя RealmRecyclerView:

    <?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_width="match_parent"
    android:layout_height="wrap_content">

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

Теперь в вашем фрагменте RealmRecycler получите результат запроса Realm для RealmObjects, раздуйте и определите 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;
                      }
                  }
          );
      }
  }
person T_Dun    schedule 11.06.2016