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

