Пейджинг 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 скоро будет улучшена, чтобы охватить эти самые основные проблемы.

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

  1. Создайте два идентичных запроса для желаемых результатов списка.
  2. Один из этих запросов возвращает только полный список результатов на основе ключа, который вы будете использовать для уникальной идентификации элемента. В моем случае это messageId.
  3. Загрузите запрос в 2 и индивидуально перебирайте список результатов, используя for...цикл до тех пор, пока вы не найдете элемент, положение которого в списке хотите узнать. Эта позиция задается итератором, который вы используете в своем блоке цикла.
  4. Передайте позицию элемента из 3 как initialKey параметр в ваш Pager построитель финального запроса
  5. Первый блок данных, который вы сейчас получите, будет содержать нужный вам элемент.
  6. Если вы хотите, вы можете теперь прокрутить до этого элемента в своем RecyclerView, но вам нужно будет запросить его из текущего списка элементов, загруженных в адаптер. См. Об использовании .snapshot() в PagingAdapter

Вот и все, теперь я могу, наконец, загрузить элемент в определенное место с помощью Paging 3 + Room, совершенно не имея представления о том, является ли это правильным способом сделать это, благодаря полностью отсутствующей документации для этого.

Другие вопросы по тегам