Realm-java: How to using custom data list after re-sorting by Comparator for RealmRecyclerViewAdapter

Created on 19 Sep 2018  路  4Comments  路  Source: realm/realm-java

I have fragment with RecyclerView.

I need the next:

  • When Realm was changed in fragment the RecyclerVeiw must autoupdate.
  • Data show in sorted order

Here my fragment:

public class MyFragment extends Fragment  {
private ListConversationAdapter sortAdapter;
private Realm realm;
private RealmResults<LSConversation> realmResults;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        realm = Realm.getDefaultInstance();
        realmResults = realm.where(LSConversation.class).findAllAsync();
        sortAdapter = new ListConversationAdapter(getActivity(), realmResults)
        sortRecyclerView.setAdapter(sortAdapter);
}

Here my adapter:

public class ListConversationAdapter extends RealmRecyclerViewAdapter<LSConversation, ListConversationAdapter.ConversationViewHolder> {

private RealmList<LSConversation> lsConversations = new RealmList<>();

public ListConversationAdapter(Context context, RealmResults<LSConversation> list) {
        super(list, true, true);
}

@NonNull
@Override
public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.item_conversation, parent, false);
        return new ConversationViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull ConversationViewHolder holder, final int position) {
        LSConversation lsConversation = getItem(position);
        if (lsConversation != null && lsConversation.isValid()) {
            // to do something
        }
}

class ConversationViewHolder extends RecyclerView.ViewHolder {

        LinearLayout item;
        AvatarView imageViewIcon;
        TextView textTitle;
        TextView textMessage;
        TextView textTimeStamp;

        ConversationViewHolder(View itemView) {
            super(itemView);
            item = itemView.findViewById(R.id.layoutConversation);
            imageViewIcon = itemView.findViewById(R.id.iconConversation);
            textTitle = itemView.findViewById(R.id.textViewConversationTitle);
            textTimeStamp = itemView.findViewById(R.id.textViewConversationTimeStamp);
            textMessage = itemView.findViewById(R.id.textViewConversationMessage);
        }
    }
}

Here my LSConversation.java:

public class LSConversation extends RealmObject {
    @PrimaryKey
    @Required
    @RealmField (name = RealmConstants.conversationId)
    private String conversationId;
    @Required
    @RealmField (name = RealmConstants.displayName)
    private String displayName;
    // type: direct (private), group (public), broadcast (broadcast)
    @Required
    @RealmField (name = RealmConstants.type)
    private String type;
    @RealmField (name = RealmConstants.repliedBroadcast)
    private boolean repliedBroadcast;
    @RealmField (name = RealmConstants.owner)
    private LSUser owner;
    @Required
    @RealmField (name = RealmConstants.lastModified)
    private Date lastModified;
    // using for filter conversation by users existed inside it
    @RealmField (name = RealmConstants.displayToUsers)
    private RealmList<LSUser> displayToUsers;
    // using for filter conversation after archive
    @RealmField (name = RealmConstants.archiveFromUsers)
    private RealmList<LSUser> archiveFromUsers;
    // using for filter conversation after delete
    @RealmField (name = RealmConstants.deleteFromUsers)
    private RealmList<LSUser> deleteFromUsers;
    // using for storage all users in conversation
    @RealmField (name = RealmConstants.activeUsers)
    private RealmList<LSUser> activeUsers;
    // using for storage all users was read conversation
    @RealmField (name = RealmConstants.seenUsers)
    private RealmList<LSUser> seenUsers;
    @RealmField (name = RealmConstants.messages)
    private RealmList<LSMessage> messages;
    // using for storage info of user after delete conversation
    @RealmField (name = RealmConstants.conversationHistoryFroms)
    private RealmList<LSConversationHistoryFrom> conversationHistoryFroms;
    @RealmField (name = RealmConstants.latestMessage)
    private LSMessage latestMessage;
    @Ignore
    private boolean isReaded;

    public String getConversationId() { return conversationId; }

    public void setConversationId(String conversationId) { this.conversationId = conversationId; }

    public String getDisplayName() { return displayName; }

    public void setDisplayName(String displayName) { this.displayName = displayName; }

    public String getType() { return type; }

    public void setType(String type) { this.type = type; }

    public boolean isRepliedBroadcast() { return repliedBroadcast; }

    public void setRepliedBroadcast(boolean repliedBroadcast) { this.repliedBroadcast = repliedBroadcast; }

    public LSUser getOwner() { return owner; }

    public void setOwner(LSUser owner) { this.owner = owner; }

    public Date getLastModified() { return lastModified; }

    public void setLastModified(Date lastModified) { this.lastModified = lastModified; }

    public RealmList<LSUser> getDisplayToUsers() { return displayToUsers; }

    public void setDisplayToUsers(RealmList<LSUser> displayToUsers) { this.displayToUsers = displayToUsers; }

    public RealmList<LSUser> getArchiveFromUsers() { return archiveFromUsers; }

    public void setArchiveFromUsers(RealmList<LSUser> archiveFromUsers) { this.archiveFromUsers = archiveFromUsers; }

    public RealmList<LSUser> getDeleteFromUsers() { return deleteFromUsers; }

    public void setDeleteFromUsers(RealmList<LSUser> deleteFromUsers) { this.deleteFromUsers = deleteFromUsers; }

    public RealmList<LSUser> getActiveUsers() { return activeUsers; }

    public void setActiveUsers(RealmList<LSUser> activeUsers) { this.activeUsers = activeUsers; }

    public RealmList<LSUser> getSeenUsers() { return seenUsers; }

    public void setSeenUsers(RealmList<LSUser> seenUsers) { this.seenUsers = seenUsers; }

    public RealmList<LSMessage> getMessages() { return messages; }

    public void setMessages(RealmList<LSMessage> messages) { this.messages = messages; }

    public RealmList<LSConversationHistoryFrom> getConversationHistoryFroms() {
        return conversationHistoryFroms;
    }

    public void setConversationHistoryFroms(RealmList<LSConversationHistoryFrom> conversationHistoryFroms) {
        this.conversationHistoryFroms = conversationHistoryFroms;
    }

    public boolean isReaded() {
        return isReaded;
    }

    public void setReaded(boolean readed) {
        isReaded = readed;
    }

    public LSMessage getLatestMessage() {
        return latestMessage;
    }

    public void setLatestMessage(LSMessage latestMessage) {
        this.latestMessage = latestMessage;
    }

    public boolean isInActive() {
        return MessageUtils.isInactiveConversation(this);
    }

    public Date getOrderBy() {
        if (isInActive()) {
            LSMessage lsMessage = MessageUtils.getLatestMessage(this);
            if (lsMessage != null) {
                return lsMessage.getTimestamp();
            } else {
                return new Date(0);
            }
        } else {
            return lastModified;
        }
    }
}

Here my LSConversationSortOrder.java

public class LSConversationSortOrder implements Comparator<LSConversation> {
    @Override
    public int compare(LSConversation lsConversation1, LSConversation lsConversation2) {
        Date date1 = lsConversation1.getOrderBy(), date2 = lsConversation2.getOrderBy();
        return (date1 != null && date2 != null) ? date2.compareTo(date1) : 0;
    }
}

And as result all work fine. Nice!
But after Realm data changed I want to sorting data list by custom Comparator LSConversationSortOrder, this property "getOrderBy" is only using in Local not sync up to realm server.
Question:
How to using RealmList data after sorting by LSConversationSortOrder for ListConversationAdapter
Something like this:
Here my adapter:

public class ListConversationAdapter extends RealmRecyclerViewAdapter<LSConversation, ListConversationAdapter.ConversationViewHolder> {

private RealmList<LSConversation> lsConversations = new RealmList<>();

public ListConversationAdapter(Context context, RealmResults<LSConversation> list) {
        super(list, true, true);
list.addChangeListener(new RealmChangeListener<RealmResults<LSConversation>>() {
            @Override
            public void onChange(RealmResults<LSConversation> lsConversations) {
                MyLogger.getInstance(TAG).info("RealmChangeListener");
                updateDataList();
            }
        });
}

private void updateDataList() {
        MyLogger.getInstance(TAG).info("updateDataList");
        if (getData() != null && getData().size() > 0) {
            lsConversations = new RealmList<>();
            lsConversations.addAll(getData());
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                lsConversations.sort(new LSConversationSortOrder());
            } else {
                Collections.sort(lsConversations, new LSConversationSortOrder());
            }
            notifyDataSetChanged();
      }
}

private LSConversation getCustomItem(int position) {
        return lsConversations != null ? lsConversations.get(position) : null;
}

@NonNull
@Override
public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.item_conversation, parent, false);
        return new ConversationViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull ConversationViewHolder holder, final int position) {
        LSConversation lsConversation = getCustomItem(position);
        if (lsConversation != null && lsConversation.isValid()) {
            // to do something
        }
}

class ConversationViewHolder extends RecyclerView.ViewHolder {

        LinearLayout item;
        AvatarView imageViewIcon;
        TextView textTitle;
        TextView textMessage;
        TextView textTimeStamp;

        ConversationViewHolder(View itemView) {
            super(itemView);
            item = itemView.findViewById(R.id.layoutConversation);
            imageViewIcon = itemView.findViewById(R.id.iconConversation);
            textTitle = itemView.findViewById(R.id.textViewConversationTitle);
            textTimeStamp = itemView.findViewById(R.id.textViewConversationTimeStamp);
            textMessage = itemView.findViewById(R.id.textViewConversationMessage);
        }
    }
}

I remind you that I need 2 things:
1.When Realm was changed in fragment the RecyclerVeiw must autoupdate.
2.Data show in sorted order
Thank you.

Version of Realm and tooling

Realm version(s):
android-adapters: 3.0.0
realm-gradle-plugin: 5.4.2

Realm sync feature enabled: no

Android Studio version: 3.1.4

Which Android version and device: 5.1.1

O-Community T-Help

Most helpful comment

@Zhuinden Than you for your answer, but I found the solution as here:

public class ListConversationAdapter extends RealmRecyclerViewAdapter<LSConversation, ListConversationAdapter.ConversationViewHolder> {

    private RealmList<LSConversation> lsConversationsRealmList = new RealmList<>();

    public ListConversationAdapter(Context context, RealmResults<LSConversation> lsConversations, LSUser myLSUser, String conversationType) {
        super(lsConversations, true);
        this.context = context;
        this.myLSUser = myLSUser;
        this.conversationType = conversationType;

        lsConversations.addChangeListener((lsConversations1, changeSet) -> {
            updateDataList();
            notifyDataSetChanged();
        });
    }

    private void updateDataList() {
        if (getData() != null && getData().size() > 0) {
            lsConversationsRealmList = new RealmList<>();
            lsConversationsRealmList.addAll(getData());
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                lsConversationsRealmList.sort(new LSConversationSortOrder());
            } else {
                Collections.sort(lsConversationsRealmList, new LSConversationSortOrder());
            }
        }
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        updateDataList();
    }

    @Nullable
    @Override
    public LSConversation getItem(int index) {
        return lsConversationsRealmList != null ? lsConversationsRealmList.get(index) : null;
    }

    @Override
    public int getItemCount() {
        return lsConversationsRealmList != null ? lsConversationsRealmList.size() : 0;
    }

    @NonNull
    @Override
    public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.item_conversation, parent, false);
        return new ConversationViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ConversationViewHolder holder, final int position) {
        LSConversation lsConversation = getItem(position);
        if (lsConversation != null && lsConversation.isValid()) {
            // to do something
        }
    }
}

It's mean: just define RealmList lsConversationsRealmList and override getItem() inside adapter.
It's working fine.
Please tell me if something wrong. Thank you.

All 4 comments

Hi, anyone can help? tks

You can only use Comparator for the list if your list is an "unmanaged RealmList".

But that clearly won't do.

        if (data != null && !data.isManaged())
            throw new IllegalStateException("Only use this adapter with managed list, " +
                    "for un-managed lists you can just use the RecyclerView.Adapter");

Therefore to execute the comparator, you need to copy the items from the Realm, this is potentially lengthy operation so it makes sense to do on background thread

try(Realm realm = Realm.getDefaultInstance()) {
    return Collections.sort(realm.copyFromRealm(realm.where().....findAll()), new LSConversationSortOrder());
}

Which means the only way to retain automatic updates if you re-evaluate this thing on a background thread each time you get a RealmResults notification for which you probably want a second RealmResults that is

 realm.where(LSConversation.class).findAllAsync()

Keep it as field reference, and add a change listener to it so that you know you have to re-calculate your list, which you can pass into your regular ListAdapter and let it diff the new items

@Zhuinden Than you for your answer, but I found the solution as here:

public class ListConversationAdapter extends RealmRecyclerViewAdapter<LSConversation, ListConversationAdapter.ConversationViewHolder> {

    private RealmList<LSConversation> lsConversationsRealmList = new RealmList<>();

    public ListConversationAdapter(Context context, RealmResults<LSConversation> lsConversations, LSUser myLSUser, String conversationType) {
        super(lsConversations, true);
        this.context = context;
        this.myLSUser = myLSUser;
        this.conversationType = conversationType;

        lsConversations.addChangeListener((lsConversations1, changeSet) -> {
            updateDataList();
            notifyDataSetChanged();
        });
    }

    private void updateDataList() {
        if (getData() != null && getData().size() > 0) {
            lsConversationsRealmList = new RealmList<>();
            lsConversationsRealmList.addAll(getData());
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                lsConversationsRealmList.sort(new LSConversationSortOrder());
            } else {
                Collections.sort(lsConversationsRealmList, new LSConversationSortOrder());
            }
        }
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        updateDataList();
    }

    @Nullable
    @Override
    public LSConversation getItem(int index) {
        return lsConversationsRealmList != null ? lsConversationsRealmList.get(index) : null;
    }

    @Override
    public int getItemCount() {
        return lsConversationsRealmList != null ? lsConversationsRealmList.size() : 0;
    }

    @NonNull
    @Override
    public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.item_conversation, parent, false);
        return new ConversationViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ConversationViewHolder holder, final int position) {
        LSConversation lsConversation = getItem(position);
        if (lsConversation != null && lsConversation.isValid()) {
            // to do something
        }
    }
}

It's mean: just define RealmList lsConversationsRealmList and override getItem() inside adapter.
It's working fine.
Please tell me if something wrong. Thank you.

Well, that's slightly trickier than what I expected, but it makes sense. I just didn't think about re-using RealmRecyclerViewAdapter and override getItem.

Was this page helpful?
0 / 5 - 0 ratings