Reputation: 4782
I have a list of messages. Each message has a unique GUID.
My setup is working for normal usage: user clicks on conversation, list opens with all the messages belonging to that conversation, ordered by most recent first.
ConversationFragment
@Override
public void onViewCreated(
@NonNull View view,
@Nullable Bundle savedInstanceState
) {
LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
viewModel = new ViewModelProvider(this).get(ConversationViewModel.class);
viewModel
.getMessageList(lifecycleOwner, conversationId) // conversationId is a global variable
.observe(lifecycleOwner, messagePagingData -> adapter.submitData(
lifecycleOwner.getLifecycle(),
messagePagingData
));
super.onViewCreated(view, savedInstanceState);
}
ConversationViewModel
final PagingConfig pagingConfig = new PagingConfig(10, 10, false, 20);
private final ConversationRepository conversationRepository;
public ConversationViewModel(@NonNull Application application) {
super(application);
conversationRepository = new ConversationRepository(application);
}
public LiveData<PagingData<ItemMessage>> getMessageList(
@NonNull LifecycleOwner lifecycleOwner,
@NonNull String conversationId
) {
return PagingLiveData.cachedIn(
PagingLiveData.getLiveData(new Pager<>(pagingConfig, () -> conversationRepository.getMessageList(conversationId))),
lifecycleOwner.getLifecycle()
);
}
ConversationRepository
private final MessageDao messageDao;
public ConversationRepository(@NonNull Context context) {
AppDatabase database = AppDatabase.getDatabase(context);
messageDao = database.messageDao();
}
public PagingSource<Integer, ItemMessage> getMessageList(@NonNull String conversationId) {
return messageDao.getMessageList(conversationId);
}
MessageDao
@Query(
"SELECT * FROM Message " +
"WHERE Message.conversationId = :conversationId " +
"ORDER BY Message.time DESC"
)
public abstract PagingSource<Integer, ItemMessage> getMessageList(String conversationId);
Now my goal is to be able to open the conversation already scrolled at a specific message.
I also do not want to load the entire conversation and then scroll to the message, some conversations can be very long and I do not want to put the user on an auto scroll that can take ages to reach the specific message.
Ideally the way I envision this being done correct is to pass the message id to be in view, load a chunk of X messages surrounding before and after that message id and then after it is already presented to the user in the RecyclerView
it will load more if the user goes up or down.
This is not meant to use network requests, the entire conversation is available in the database already so it will only use the information that is already in the database.
I've tried understanding the examples that use ItemKeyedDataSource
or PageKeyedDataSource
, but I cannot go anywhere because every single time those examples are in Kotlin only and require Retrofit to work, which I do not use. As it is these examples are completely useless for anyone like me that is in Java and not using Retrofit.
How can this be achieved?
Please provide an answer in Java, not just Kotlin only (kotlin is OK as long as it's in java as well) and please do not suggest new libraries.
Upvotes: 6
Views: 3756
Reputation: 4782
As far as I could find the official documentation does not provide any sort of clue on how to solve this one for a Paging + Room integration. In fact, it doesn't provide any solution whatsoever to scroll to an item in a PagingDataAdapter
, period.
The only thing that worked for me so far was to run two queries every single time I wish to accomplish this: one to find the item position in the result query list and the other to actually load said list with the initialKey
set in the Pager
constructor with the value of the item position we queried previously.
And if you're feeling a bit confused, this does not end here, because even the explanation for what is initialKey
and how to use it is just not documented. No, seriously: What does the initialKey parameter do in the Pager constructor
So there's two guessing games here: one to find a proper way to lookup the item index from a result list and another to set it up properly in the final query.
I hope the Paging 3 documentation gets improved soon to cover these very basic issues.
In the end this is an example of how I managed to get this problem kind of working for me, even though I have no idea if this is the proper way to do it because, again, their documentation is absolutely lacking in this department.
messageId
.for...
loop until you find the item you want to know its position in the list. That position is given by the iterator you use in your loop block.initialKey
parameter into your Pager
builder of the final queryRecyclerView
, but you'll have to query it from the current list of items loaded in the adapter. See about using the .snapshot()
in the PagingAdapter
That's it, now I can finally load an item at a certain position using Paging 3 + Room, with absolutely no idea of whether this is the proper way to do it thanks to the completely absent documentation for this.
Upvotes: 3