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

在Fragment中调用setTheme()切换App主题出现无限循环如何解决

问题原因

  • 调用AppCompatDelegate.setDefaultNightMode()本身就会自动触发宿主Activity重建,你同时又手动调用getActivity().recreate(),双重重建触发无限循环
  • 主题设置时机错误:setTheme()方法必须在Activity调用setContentView()之前执行才会生效,你把这段逻辑写在Fragment的onViewCreated中,此时Activity布局早已加载完成,主题设置本身就无法生效,反而每次重建都会重复触发配置变更,加剧循环
  • Fragment的onCreateView写法错误:你已经通过ViewBinding inflate生成了binding对象,但是最后返回的是重新inflate的布局文件,导致给binding.switch设置的监听没有绑定到实际展示的控件上;同时重建后开关状态回显时会再次触发OnCheckedChangeListener,重新走主题切换逻辑,形成循环
  • 没有优先从SharedPreferences读取已保存的主题配置,每次重建时判断逻辑混乱,反复触发配置变更

修复方案

  1. 将主题设置逻辑迁移到宿主Activity的onCreate方法中,放在super.onCreate之后、setContentView之前执行:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 先读取SharedPreferences中保存的主题配置
    SharedPreferences sp = getSharedPreferences("app", Context.MODE_PRIVATE);
    String themeName = sp.getString("ThemeName", "Mode 1");
    if (themeName.equalsIgnoreCase("Mode 1")) {
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
        setTheme(R.style.Theme_One);
    } else {
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
        setTheme(R.style.Theme_Two);
    }
    // 再加载布局
    setContentView(R.layout.activity_xxx);
}
  1. 修正Fragment的onCreateView写法,返回ViewBinding生成的根布局,设置开关监听前先给开关赋值初始状态,避免默认状态变更触发不必要的监听回调:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    binding = FragmentXYZBinding.inflate(inflater, container, false);
    // 读取已有配置给开关设初始值,此时还未设置监听,不会触发回调
    SharedPreferences sp = getActivity().getSharedPreferences("app", Context.MODE_PRIVATE);
    String currentTheme = sp.getString("ThemeName", "Mode 1");
    binding.switch.setChecked(currentTheme.equalsIgnoreCase("Mode 2"));
    // 设置开关监听
    binding.switch.setOnCheckedChangeListener((buttonView, isChecked) -> {
        setTheme(isChecked ? "Mode 2" : "Mode 1");
    });
    // 返回binding的根布局,不要重新inflate布局
    return binding.getRoot();
}
  1. 删除Fragment的onViewCreated中的主题设置逻辑,以及setTheme方法中的手动recreate()调用:setDefaultNightMode本身已经会触发Activity重建,不需要额外调用
public void setTheme(String name) {
    SharedPreferences preferences = getActivity().getSharedPreferences("app", Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = preferences.edit();
    editor.putString("ThemeName", name);
    editor.apply();
}

如果你不需要切换夜间模式,只是切换普通自定义主题,可删除Activity中setDefaultNightMode相关代码,此时再在setTheme方法末尾添加getActivity().recreate()即可,不会触发循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 17:54:05