1

我有两个片段的简单设置:ConversationFragmentDetailsFragment

我正在使用RoomPaging 3 library填充ConversationFragment我正在使用的PagingLiveData实现以及AndroidViewModel属于ConversationFragment.

我没有使用Navigation Components这里,只是根据 Android 文档的常见片段导航。

从那个片段我可以打开DetailsFragment然后再次返回片段。一切都运行良好,直到我打开所述片段并返回,然后绑定在 中的观察者ConversationFragment丢失,因为在打开DetailsFragment.

到目前为止,这不是一个大问题,我可以再次重新启动观察者,并且当我这样做时它确实有效。但是,当我再次附加观察者时,整个列表重排,这会导致列表中的项目RecyclerView变得疯狂,列表所在的位置丢失并且滚动条改变大小,确认页面正在加载/重新加载。

我可以在一定程度上承受这种奇怪的行为,但在此之上失去位置是不可接受的。

我研究了在视图模型中缓存结果,但我可以在可用文档中找到的示例是基本的,并没有显示如何使用LiveData<PagingData<...>对象实现相同的目标。

目前这就是我所拥有的:

ConversationFragment

    @Override
    public void onViewCreated(
        @NonNull View view,
        @Nullable Bundle savedInstanceState
    ) {

        if (viewModel == null) {
            viewModel = new ViewModelProvider(this).get(ConversationViewModel.class);
        }

        if (savedInstanceState == null) {
            // adapter is initialized in onCreateView
            viewModel
.getList(getViewLifecycleOwner())
.observe(getViewLifecycleOwner(), pagingData -> adapter.submitData(lifecycleOwner.getLifecycle(), pagingData));
        }

        super.onViewCreated(view, savedInstanceState);

    }

ConversationViewModel

public class ConversationViewModel extends AndroidViewModel {

    final PagingConfig pagingConfig = new PagingConfig(10, 10, false, 20);
    private final Repository repository;
    private final MutableLiveData<PagingData<ItemView>> messageList;

    public ConversationFragmentVM(@NonNull Application application) {
        super(application);
        messageList = new MutableLiveData<>();
        repository = new Repository(application);
    }

    public LiveData<PagingData<ItemView>> getList(@NonNull LifecycleOwner lifecycleOwner) {

        // at first I tried only setting the value if it was null
        // but since the observer is lost on destroy and the value
        // is not it would never be able to "restart" the observer
        // again
//        if (messageList.getValue() == null) {
            PagingLiveData.cachedIn(
                PagingLiveData.getLiveData(new Pager<>(pagingConfig, () -> repository.getMessageList())),
                lifecycleOwner.getLifecycle()
            ).observe(lifecycleOwner, messageList::setValue);
//        }

        return messageList;

    }

}

事实上,即使我返回PagingLiveData.cachedIn片段时的行为结果也是一样的;这些项目在 recyclerview 列表中显示出不稳定的行为,并且它所在的位置完全丢失了。

这是我试图实现的,看看它是否解决了我的问题: 在此处输入图像描述

这是此处提供的代码实验室:https ://developer.android.com/codelabs/android-training-livedata-viewmodel#8

正如您所看到的mAllWords,它们被缓存并且它们仅在第一次构造视图模型时才被初始化,任何后续更改都只是更新,并且只需要在片段被销毁并再次创建时附加新的观察者而仍在回栈。

这就是我想要做的,但它并没有像我想象的那样工作,至少它不像我想象的那么简单。

如何做到这一点?

4

1 回答 1

4

这里有很多东西要解压,但我最好的猜测是你getListConversationViewModel. 您在使用 ViewModels 和 LiveData 跨导航保存数据的正确轨道上,但在这里您每次调用此方法时都重新创建 LiveData,这意味着当您恢复ConversationFragment并被onViewCreated调用时,它会创建一个新的 Pager 来获取新数据。

解决方案是在第一次创建寻呼机时ConversationViewModel创建,然后访问 LiveData 对象本身,而不是方法。您可以在 Codelab 示例中看到这一点,他们在构造函数中分配 LiveData,并在getAllWords()方法中简单地返回已经创建的 LiveData。

以此为例,更改ConversationViewModel为类似的内容并将其更改为使用您的配置和存储库。

private final LiveData<PagingData<ItemView>> messageList;

public ConversationFragmentVM(@NonNull Application application) {
    super(application);
    repository = new Repository(application);

    // CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
    CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
    Pager<Integer, User> pager = Pager<>(
      new PagingConfig(/* pageSize = */ 20),
      () -> ExamplePagingSource(backend, query));

    messageList = PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), viewModelScope);
}

public LiveData<PagingData<ItemView>> getList(){
    return messageList;
}

然后在您的片段中,您只需像往常一样观察 getList() ,除了这次它返回一个预先存在的版本。

viewModel.getList().observe(getViewLifecycleOwner(), pagingData -> 
    adapter.submitData(lifecycleOwner.getLifecycle(), pagingData));
}

我无法测试它是否可以编译或工作,所以如果没有,请告诉我,我会更新这个答案。

于 2020-12-11T10:18:35.607 回答