Android Oreo中Toolbar与NavigationDrawer随Locale切换不改变布局方向
这个问题我之前适配Android 8.0(API26+)时也踩过坑,核心原因是从Oreo开始,系统对Configuration的管理逻辑发生了变化——之前直接调用updateConfiguration的方式,已经无法覆盖ActionBar(包括Toolbar)、DrawerLayout这类依赖全局/系统Context的组件配置了。它们会优先读取系统Locale,而非我们应用内自定义的配置。下面是具体的解决步骤:
1. 重写Activity的attachBaseContext,确保启动时应用自定义Locale
这是关键一步,因为API26+推荐通过createConfigurationContext为Activity创建专属的配置Context,这样整个Activity的所有组件(包括Toolbar、DrawerLayout)都会使用正确的Locale配置。
@Override protected void attachBaseContext(Context newBase) { // 从持久化存储(比如SharedPreferences)读取你之前保存的应用Locale SharedPreferences prefs = newBase.getSharedPreferences("AppSettings", MODE_PRIVATE); String localeTag = prefs.getString("selected_locale", Locale.getDefault().toLanguageTag()); Locale targetLocale = Locale.forLanguageTag(localeTag); Configuration config = new Configuration(newBase.getResources().getConfiguration()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { config.setLocale(targetLocale); config.setLayoutDirection(targetLocale); // 创建新的Context并应用配置 newBase = newBase.createConfigurationContext(config); } else { // 兼容Oreo以下版本 config.locale = targetLocale; newBase.getResources().updateConfiguration(config, newBase.getResources().getDisplayMetrics()); } super.attachBaseContext(newBase); }
2. 修改setLocale方法,持久化Locale并重启Activity
原来的setLocale只是临时修改了Resources,但没有持久化配置,而且重启方式没问题,但要确保把选中的Locale存起来,让attachBaseContext能读取到:
private void setLocale(Locale locale) { // 持久化保存选中的Locale SharedPreferences prefs = getSharedPreferences("AppSettings", MODE_PRIVATE); prefs.edit().putString("selected_locale", locale.toLanguageTag()).apply(); // 重启Activity(保持你原来的方式即可) Intent intent = getIntent(); startActivity(intent); finish(); }
3. 确保UI组件用Activity的Context初始化
检查你的initToolbar和initNavigationDrawer方法,不要使用getApplicationContext()来inflayout或者初始化组件——Application的Context不会被attachBaseContext修改,所以会导致组件读取系统Locale。你当前的代码里用getLayoutInflater()是正确的,因为它绑定的是当前Activity的Context,已经应用了自定义配置。
如果还是有问题,可以手动给DrawerLayout设置方向(保险起见):
// 在initNavigationDrawer方法中添加 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { // 这里的locale是你选中的目标Locale Drawer.setLayoutDirection(locale); }
为什么之前的方法在Oreo上失效?
API26开始,Android引入了Context级别的Configuration隔离,每个Context可以拥有独立的配置。之前调用res.updateConfiguration()只会修改当前Activity的Resources,但Toolbar作为ActionBar的实现,内部可能依赖了Application Context或者系统服务的配置,这些没有被更新。而通过attachBaseContext在Activity创建时就注入自定义配置的Context,能确保所有关联组件都使用正确的Locale和布局方向。
内容的提问来源于stack exchange,提问作者Muhammed Refaat

