You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ViewModel更新时Fragment内RecyclerView自动复位至初始位置(未重建RecyclerView或调用notifyDataSetChanged)

ViewModel更新时Fragment内RecyclerView自动复位至初始位置(未重建RecyclerView或调用notifyDataSetChanged)

我仔细看了你的代码,发现问题主要出在ViewPager2的Fragment适配器逻辑错误,以及Tab文本更新触发的ViewPager2内部状态变化,具体来说:

1. 适配器创建Fragment的逻辑完全写反了

看你PlannerFragmentViewPager2Adaptor里的createFragment方法,比如第0个位置的代码:

if(localeChoosingFragment !=null) localeChoosingFragment = new LocaleChoosingFragment(mContext, japanGuideViewModel);
return localeChoosingFragment;

这逻辑是如果Fragment已经存在,反而重新新建一个!正常逻辑应该是如果Fragment为null才新建,否则复用已有的实例。

当你调用tab.setText()更新第二个Tab的文本时,TabLayoutMediator会触发ViewPager2的内部更新,导致适配器重新调用createFragment方法。此时因为你的逻辑错误,会新建一个LocaleChoosingFragment实例——虽然你觉得Fragment没重建,但实际上已经被替换成新的了,新的Fragment里的RecyclerView自然会回到初始位置。

修正这个逻辑

把适配器里的判断反过来,改为复用已有的Fragment实例:

@Override
public Fragment createFragment(int position) {
    Log.d(TAG, "createFragment called in plannerFragment. Position is " + position);
    switch (position) {
        case 0:
            Log.d(TAG, "planner fragment, case 0, localechoosingfragment");
            // 如果实例为空才新建,否则复用
            if(localeChoosingFragment == null) {
                localeChoosingFragment = new LocaleChoosingFragment(mContext, japanGuideViewModel);
            }
            return localeChoosingFragment;

        case 1:
            Log.d("planner fragment localeExploringFragmentTAG", "case 1");
            if(localeExploringFragment == null) {
                localeExploringFragment = new LocaleExploringFragment(mContext, japanGuideViewModel);
            }
            return localeExploringFragment;

        default:
            Log.d(TAG, "default constructor so returning localeChoosingFragment");
            if(localeChoosingFragment == null) {
                localeChoosingFragment = new LocaleChoosingFragment(mContext, japanGuideViewModel);
            }
            return localeChoosingFragment;
    }
}

2. 额外建议:保存RecyclerView的滚动位置

即使修复了适配器逻辑,有时候ViewPager2的Fragment切换、页面回收也可能导致滚动位置丢失。你可以在LocaleChoosingFragment里手动保存和恢复滚动位置:

在LocaleChoosingFragment里添加变量保存位置:

private int savedScrollPosition = 0;

然后在Fragment的生命周期方法里保存位置,比如onPause:

@Override
public void onPause() {
    super.onPause();
    // 保存当前可见的第一个item位置
    savedScrollPosition = recyclerViewLayoutManager.findFirstVisibleItemPosition();
}

在onViewCreated里恢复位置:

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

    Log.d(TAG, "onviewcreated in locale choosing fragment.");

    localeChoosingAdaptor = new LocaleChoosingRecyclerViewAdaptor(mContext, japanGuideViewModel);
    SnapHelper = new SnapHelperOneByOne();
    recyclerViewLayoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false);
    localeChoosingRecyclerView.setLayoutManager(recyclerViewLayoutManager);
    localeChoosingRecyclerView.setAdapter(localeChoosingAdaptor);
    SnapHelper.attachToRecyclerView(localeChoosingRecyclerView);
    
    // 恢复之前的滚动位置
    if(savedScrollPosition != 0) {
        localeChoosingRecyclerView.scrollToPosition(savedScrollPosition);
    }
}

这样即使Fragment被临时回收再重建,也能回到之前的滚动位置。

3. 补充说明

你提到Fragment没有被重建,但实际上因为适配器的错误逻辑,每次触发createFragment都会新建实例,所以Fragment是被重建了的——只是你可能没注意到日志里的constructor called for localeChoosingFragment会重复打印。修复适配器逻辑后,这个问题应该就能解决。

备注:内容来源于stack exchange,提问作者Jesse Duffield

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 12:39:29