Пейджинг 3: как загрузить список в позиции элемента или в элементе с определенным идентификатором
У меня есть список сообщений. Каждое сообщение имеет уникальный идентификатор GUID.
Моя настройка работает в обычном режиме: пользователь нажимает на беседу, открывается список со всеми сообщениями, относящимися к этому разговору, в порядке убывания количества последних.
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);
}
Сообщение
@Query(
"SELECT * FROM Message " +
"WHERE Message.conversationId = :conversationId " +
"ORDER BY Message.time DESC"
)
public abstract PagingSource<Integer, ItemMessage> getMessageList(String conversationId);
Теперь моя цель - иметь возможность открывать диалог, уже прокрученный по определенному сообщению.
Я также не хочу загружать весь разговор, а затем прокручивать его до сообщения, некоторые разговоры могут быть очень длинными, и я не хочу, чтобы пользователь находился в режиме автоматической прокрутки, который может занять много времени, чтобы добраться до конкретного сообщения.
В идеале, как я предполагаю, это будет сделано правильно - это передать идентификатор сообщения, который будет отображаться, загрузить фрагмент X сообщений, окружающих до и после этого идентификатора сообщения, а затем после того, как он уже представлен пользователю в
RecyclerView
он будет загружать больше, если пользователь пойдет вверх или вниз.
Это не предназначено для использования сетевых запросов, весь диалог уже доступен в базе данных, поэтому он будет использовать только ту информацию, которая уже есть в базе данных.
Я пытался понять примеры, в которых используется
ItemKeyedDataSource
или же
PageKeyedDataSource
, но я не могу никуда идти, потому что каждый раз эти примеры находятся только в Kotlin и требуют для работы Retrofit, который я не использую. Эти примеры совершенно бесполезны для тех, кто вроде меня использует Java и не использует Retrofit.
Как этого добиться?
Пожалуйста, дайте ответ на Java, а не только на Kotlin (kotlin в порядке, если он также находится в java) и, пожалуйста, не предлагайте новые библиотеки.
1 ответ
Насколько я мог найти, официальная документация не дает никаких подсказок о том, как решить эту проблему для интеграции Paging + Room. Фактически, он не предоставляет никакого решения для прокрутки к элементу в
PagingDataAdapter
, период.
Единственное, что у меня до сих пор работало, - это запускать два запроса каждый раз, когда я хочу это сделать: один для поиска позиции элемента в списке результатов запроса, а другой для фактической загрузки указанного списка с помощью
initialKey
установлен в
Pager
конструктор со значением позиции элемента, который мы запросили ранее.
И если вы чувствуете себя немного смущенным, это еще не все, потому что даже объяснение того, что
initialKey
а как его использовать просто не задокументировано. Нет, серьезно: что делает параметр initialKey в конструкторе Pager
Итак, здесь есть две игры в угадайку: одна для поиска правильного способа поиска индекса элемента в списке результатов, а другая для правильной настройки его в окончательном запросе.
Я надеюсь, что документация Paging 3 скоро будет улучшена, чтобы охватить эти самые основные проблемы.
В конце концов, это пример того, как мне удалось заставить эту проблему работать для меня, хотя я понятия не имею, правильный ли это способ сделать это, потому что, опять же, их документация абсолютно отсутствует в этом отделе.
- Создайте два идентичных запроса для желаемых результатов списка.
- Один из этих запросов возвращает только полный список результатов на основе ключа, который вы будете использовать для уникальной идентификации элемента. В моем случае это
messageId
. - Загрузите запрос в 2 и индивидуально перебирайте список результатов, используя
for...
цикл до тех пор, пока вы не найдете элемент, положение которого в списке хотите узнать. Эта позиция задается итератором, который вы используете в своем блоке цикла. - Передайте позицию элемента из 3 как
initialKey
параметр в вашPager
построитель финального запроса - Первый блок данных, который вы сейчас получите, будет содержать нужный вам элемент.
- Если вы хотите, вы можете теперь прокрутить до этого элемента в своем
RecyclerView
, но вам нужно будет запросить его из текущего списка элементов, загруженных в адаптер. См. Об использовании.snapshot()
вPagingAdapter
Вот и все, теперь я могу, наконец, загрузить элемент в определенное место с помощью Paging 3 + Room, совершенно не имея представления о том, является ли это правильным способом сделать это, благодаря полностью отсутствующей документации для этого.