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

如何实现随Switch状态动态增删Tab页Fragment?现有代码仅重启生效

动态更新ViewPager与TabLayout实现NSFW Fragment的实时添加/移除

需求场景:通过Switch开关控制NSFW Tab页的显示/隐藏,开启时添加NSFW Fragment,关闭时移除;当前已用SharedPreferences存储状态,但仅在应用重启后生效,需实现无需重启即可实时更新Tab页的效果。


解决方案步骤

1. 改造 FragmentViewPagerAdapter,支持动态修改Fragment列表

原适配器仅在构造时初始化Fragment,现在需要添加动态更新方法,并确保FragmentStateAdapter能正确识别列表变化(必须重写getItemId和containsItem):

public class FragmentViewPagerAdapter extends FragmentStateAdapter {
    private final List<Fragment> fragmentList = new ArrayList<>();
    private final List<Long> fragmentIds = new ArrayList<>(); // 标记每个Fragment的唯一ID

    public FragmentViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
        super(fragmentManager, lifecycle);
        // 初始化基础Fragment(Meme、Art、Settings)
        addFragment(new MemeFragment());
        addFragment(new ArtFragment());
        addFragment(new SettingsFragment());
    }

    // 辅助方法:添加Fragment并生成唯一ID
    private void addFragment(Fragment fragment) {
        fragmentList.add(fragment);
        fragmentIds.add(System.currentTimeMillis()); // 用时间戳保证唯一性
    }

    // 动态更新NSFW状态
    public void updateNsfwState(boolean isEnabled) {
        boolean hasNsfw = fragmentList.stream().anyMatch(f -> f instanceof NsfwFragment);
        
        if (isEnabled && !hasNsfw) {
            // 开启时:在Art和Settings之间插入NSFW
            fragmentList.add(2, new NsfwFragment());
            fragmentIds.add(2, System.currentTimeMillis());
        } else if (!isEnabled && hasNsfw) {
            // 关闭时:移除NSFW Fragment
            int nsfwIndex = fragmentList.indexOf(fragmentList.stream()
                .filter(f -> f instanceof NsfwFragment).findFirst().orElse(null));
            if (nsfwIndex != -1) {
                fragmentList.remove(nsfwIndex);
                fragmentIds.remove(nsfwIndex);
            }
        }
        notifyDataSetChanged(); // 通知适配器更新UI
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return fragmentList.get(position);
    }

    @Override
    public int getItemCount() {
        return fragmentList.size();
    }

    // 必须重写:让适配器识别每个Fragment的唯一身份
    @Override
    public long getItemId(int position) {
        return fragmentIds.get(position);
    }

    @Override
    public boolean containsItem(long itemId) {
        return fragmentIds.contains(itemId);
    }
}

2. 修改 MainActivity,处理状态更新与Tab关联

初始化适配器时不再依赖初始状态,添加回调接口接收开关变化,并重新关联TabLayout:

public class MainActivity extends AppCompatActivity implements SettingsFragment.NsfwSwitchChangeListener {
    private TabLayout tabLayout;
    private ViewPager2 viewPager;
    private FragmentViewPagerAdapter viewPagerAdapter;
    private SharedPreferences mSharedPrefs_nsfw_settings;
    private static final String SHARED_PREFS_NAME_NSFW = "nsfw_prefs";
    private static final String REWARD_GRANTED_KEY = "reward_granted";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tabLayout = findViewById(R.id.tabLayout);
        viewPager = findViewById(R.id.viewPager);
        mSharedPrefs_nsfw_settings = getSharedPreferences(SHARED_PREFS_NAME_NSFW, Context.MODE_PRIVATE);

        // 初始化适配器(仅添加基础Fragment)
        viewPagerAdapter = new FragmentViewPagerAdapter(getSupportFragmentManager(), getLifecycle());
        viewPager.setAdapter(viewPagerAdapter);

        // 关联TabLayout与ViewPager
        setupTabMediator();

        // 读取初始状态并更新适配器
        boolean initialState = mSharedPrefs_nsfw_settings.getBoolean(REWARD_GRANTED_KEY, false);
        viewPagerAdapter.updateNsfwState(initialState);
    }

    // 配置TabLayout与ViewPager的关联逻辑
    private void setupTabMediator() {
        tabLayout.removeAllTabs(); // 先清空旧Tab
        new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
            Fragment currentFragment = viewPagerAdapter.createFragment(position);
            switch (position) {
                case 0: tab.setText("Meme"); break;
                case 1: tab.setText("Art"); break;
                case 2: 
                    tab.setText(currentFragment instanceof NsfwFragment ? "NSFW" : "Settings");
                    break;
                case 3: tab.setText("Settings"); break;
            }
        }).attach();
    }

    // 实现SettingsFragment的回调接口,处理开关变化
    @Override
    public void onNsfwSwitchChanged(boolean isChecked) {
        // 更新SharedPreferences
        SharedPreferences.Editor editor = mSharedPrefs_nsfw_settings.edit();
        editor.putBoolean(REWARD_GRANTED_KEY, isChecked);
        editor.apply();

        // 更新适配器状态
        viewPagerAdapter.updateNsfwState(isChecked);
        // 重新关联TabLayout,适配新的Tab数量和标题
        setupTabMediator();
    }
}

3. 修改 SettingsFragment,添加回调传递开关状态

定义回调接口,将开关变化事件传递给MainActivity:

public class SettingsFragment extends Fragment {
    private Switch nsfwSetting;
    private SharedPreferences mSharedPrefs_nsfw_settings;
    private static final String SHARED_PREFS_NAME_NSFW = "nsfw_prefs";
    private static final String REWARD_GRANTED_KEY = "reward_granted";

    // 回调接口:通知宿主Activity开关状态变化
    public interface NsfwSwitchChangeListener {
        void onNsfwSwitchChanged(boolean isChecked);
    }

    private NsfwSwitchChangeListener listener;

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        // 确保宿主Activity实现了回调接口
        try {
            listener = (NsfwSwitchChangeListener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " 必须实现NsfwSwitchChangeListener接口");
        }
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_settings, container, false);
        nsfwSetting = view.findViewById(R.id.nsfw_switch);
        mSharedPrefs_nsfw_settings = requireContext().getSharedPreferences(SHARED_PREFS_NAME_NSFW, Context.MODE_PRIVATE);

        nsfwSetting.setOnCheckedChangeListener((buttonView, isChecked) -> {
            // 通过回调通知MainActivity
            if (listener != null) {
                listener.onNsfwSwitchChanged(isChecked);
            }
        });

        // 初始化开关状态
        nsfwSetting.setChecked(mSharedPrefs_nsfw_settings.getBoolean(REWARD_GRANTED_KEY, false));
        return view;
    }
}

核心逻辑说明

  • 适配器动态更新:通过getItemId和containsItem保证FragmentStateAdapter能正确识别Fragment的增删,避免复用错误的实例。
  • TabLayout重新关联:每次Fragment数量变化后,清空旧Tab并重新设置关联,确保Tab标题和数量与Fragment列表同步。
  • 回调通信:通过接口实现Fragment与Activity的状态传递,避免直接耦合,保证代码可维护性。

内容的提问来源于stack exchange,提问作者Vasant Raval

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:37:33