Android API 24 及更高版本中区域设置更改的 RTL 问题
Android RTL issue in API 24 and higher on locale change
我试图在运行时更改应用程序的语言环境。它在 API 24 级以下的 Andorid 中工作正常。但是在 API 24 级或更高级别中,布局方向不会根据语言环境而改变。
下面是在运行时更改语言环境的代码。我使用 LocaleHelper class 如下
public class LocaleHelper {
private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";
public static Context onAttach(Context context) {
String lang = getPersistedData(context, Locale.getDefault().getLanguage());
return setLocale(context, lang);
}
public static Context onAttach(Context context, String defaultLanguage) {
String lang = getPersistedData(context, defaultLanguage);
return setLocale(context, lang);
}
public static String getLanguage(Context context) {
return getPersistedData(context, Locale.getDefault().getLanguage());
}
public static Context setLocale(Context context, String language) {
persist(context, language);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return updateResources(context, language);
}
return updateResourcesLegacy(context, language);
}
private static String getPersistedData(Context context, String defaultLanguage) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
}
private static void persist(Context context, String language) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(SELECTED_LANGUAGE, language);
editor.apply();
}
@TargetApi(Build.VERSION_CODES.N)
private static Context updateResources(Context context, String language) {
Locale locale = new Locale(language);
Log.d("LocaleHelper", "language : "+language);
Locale.setDefault(locale);
Configuration configuration = context.getResources().getConfiguration();
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
return context.createConfigurationContext(configuration);
}
@SuppressWarnings("deprecation")
private static Context updateResourcesLegacy(Context context, String language) {
Locale locale = new Locale(language);
Locale.setDefault(locale);
Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
configuration.locale = locale;
configuration.setLayoutDirection(locale);
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
return context;
}
}
下面是我在 activity class
中使用的代码
public class MainActivity extends AppCompatActivity {
@BindView(R.id.titleTextView)
TextView mTitleTextView;
@BindView(R.id.descTextView)
TextView mDescTextView;
@BindView(R.id.aboutTextView)
TextView mAboutTextView;
@BindView(R.id.toTRButton)
Button mToTRButton;
@BindView(R.id.toENButton)
Button mToENButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
setTitle(getString(R.string.main_activity_toolbar_title));
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleHelper.onAttach(base));
}
@OnClick(R.id.toTRButton)
public void onChangeToTRClicked() {
updateViews("ur");
}
@OnClick(R.id.toENButton)
public void onChangeToENClicked() {
updateViews("en");
}
private void updateViews(String languageCode) {
Context context = LocaleHelper.setLocale(this, languageCode);
Resources resources = context.getResources();
mTitleTextView.setText(resources.getString(R.string.main_activity_title));
mDescTextView.setText(resources.getString(R.string.main_activity_desc));
mAboutTextView.setText(resources.getString(R.string.main_activity_about));
mToTRButton.setText(resources.getString(R.string.main_activity_to_tr_button));
mToENButton.setText(resources.getString(R.string.main_activity_to_en_button));
setTitle(resources.getString(R.string.main_activity_toolbar_title));
this.recreate();
}
}
并且在我的应用程序中 class 我添加了以下代码
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleHelper.onAttach(base, "en"));
}
现在,当我将区域设置从英语更改为乌尔都语时,语言发生了变化,但布局方向并未按预期发生变化。当我再次单击乌尔都语时,布局方向发生变化(第二次尝试)。以下截图供参考
请帮忙解决问题
问题似乎是它没有反映第一次更新时布局方向的变化。我通过重写 Activity
的 onAttachedToWindow
方法解决了这个问题,如下所示:
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getWindow().getDecorView().setLayoutDirection(
"ur".equals(LocaleHelper.getLanguage(this)) ?
View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
}
}
在 API 25 上测试过,运行良好。请小心,尽管我目前不确定这种方法是否有任何副作用。不过,我想这就是你要找的。
我还将更新 blog post 以反映更优雅、通用的代码。
package www.ourshopee.com.utils;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.RequiresApi;
import android.util.Log;
import java.util.Locale;
public class LocaleHelper {
private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";
public static void onCreate(Context context) {
String lang = getPersistedData(context, Locale.getDefault().getLanguage());
setLocale(context, lang);
}
public static void onCreate(Context context, String defaultLanguage) {
String lang = getPersistedData(context, defaultLanguage);
setLocale(context, lang);
}
public static String getLanguage(Context context) {
return getPersistedData(context, Locale.getDefault().getLanguage());
}
public static void setLocale(Context context, String language) {
persist(context, language);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
updateResources(context, language);
}
updateResourcesLegacy(context, language);
}
private static String getPersistedData(Context context, String defaultLanguage) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
}
private static void persist(Context context, String language) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(SELECTED_LANGUAGE, language);
editor.apply();
}
// private static void updateResources(Context context, String language) {
//
//
//
//
//
// Locale locale = new Locale(language);
// Locale.setDefault(locale);
// Configuration config = context.getResources().getConfiguration();
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// config.setLocale(locale);
// }
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// context.createConfigurationContext(config);
// }
// context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
//
//
//
//
//
//
// }
@TargetApi(Build.VERSION_CODES.N)
private static Context updateResources(Context context, String language) {
Locale locale = new Locale(language);
Log.d("LocaleHelper", "language above 24: "+language);
Locale.setDefault(locale);
Configuration configuration = context.getResources().getConfiguration();
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
return context.createConfigurationContext(configuration);
}
@SuppressWarnings("deprecation")
private static Context updateResourcesLegacy(Context context, String language) {
Locale locale = new Locale(language);
Log.d("LocaleHelper", "language below 24: "+language);
Locale.setDefault(locale);
Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
configuration.locale = locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLayoutDirection(locale);
}
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
return context;
}
}
我试图在运行时更改应用程序的语言环境。它在 API 24 级以下的 Andorid 中工作正常。但是在 API 24 级或更高级别中,布局方向不会根据语言环境而改变。 下面是在运行时更改语言环境的代码。我使用 LocaleHelper class 如下
public class LocaleHelper {
private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";
public static Context onAttach(Context context) {
String lang = getPersistedData(context, Locale.getDefault().getLanguage());
return setLocale(context, lang);
}
public static Context onAttach(Context context, String defaultLanguage) {
String lang = getPersistedData(context, defaultLanguage);
return setLocale(context, lang);
}
public static String getLanguage(Context context) {
return getPersistedData(context, Locale.getDefault().getLanguage());
}
public static Context setLocale(Context context, String language) {
persist(context, language);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return updateResources(context, language);
}
return updateResourcesLegacy(context, language);
}
private static String getPersistedData(Context context, String defaultLanguage) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
}
private static void persist(Context context, String language) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(SELECTED_LANGUAGE, language);
editor.apply();
}
@TargetApi(Build.VERSION_CODES.N)
private static Context updateResources(Context context, String language) {
Locale locale = new Locale(language);
Log.d("LocaleHelper", "language : "+language);
Locale.setDefault(locale);
Configuration configuration = context.getResources().getConfiguration();
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
return context.createConfigurationContext(configuration);
}
@SuppressWarnings("deprecation")
private static Context updateResourcesLegacy(Context context, String language) {
Locale locale = new Locale(language);
Locale.setDefault(locale);
Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
configuration.locale = locale;
configuration.setLayoutDirection(locale);
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
return context;
}
}
下面是我在 activity class
中使用的代码public class MainActivity extends AppCompatActivity {
@BindView(R.id.titleTextView)
TextView mTitleTextView;
@BindView(R.id.descTextView)
TextView mDescTextView;
@BindView(R.id.aboutTextView)
TextView mAboutTextView;
@BindView(R.id.toTRButton)
Button mToTRButton;
@BindView(R.id.toENButton)
Button mToENButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
setTitle(getString(R.string.main_activity_toolbar_title));
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleHelper.onAttach(base));
}
@OnClick(R.id.toTRButton)
public void onChangeToTRClicked() {
updateViews("ur");
}
@OnClick(R.id.toENButton)
public void onChangeToENClicked() {
updateViews("en");
}
private void updateViews(String languageCode) {
Context context = LocaleHelper.setLocale(this, languageCode);
Resources resources = context.getResources();
mTitleTextView.setText(resources.getString(R.string.main_activity_title));
mDescTextView.setText(resources.getString(R.string.main_activity_desc));
mAboutTextView.setText(resources.getString(R.string.main_activity_about));
mToTRButton.setText(resources.getString(R.string.main_activity_to_tr_button));
mToENButton.setText(resources.getString(R.string.main_activity_to_en_button));
setTitle(resources.getString(R.string.main_activity_toolbar_title));
this.recreate();
}
}
并且在我的应用程序中 class 我添加了以下代码
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleHelper.onAttach(base, "en"));
}
现在,当我将区域设置从英语更改为乌尔都语时,语言发生了变化,但布局方向并未按预期发生变化。当我再次单击乌尔都语时,布局方向发生变化(第二次尝试)。以下截图供参考
请帮忙解决问题
问题似乎是它没有反映第一次更新时布局方向的变化。我通过重写 Activity
的 onAttachedToWindow
方法解决了这个问题,如下所示:
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getWindow().getDecorView().setLayoutDirection(
"ur".equals(LocaleHelper.getLanguage(this)) ?
View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
}
}
在 API 25 上测试过,运行良好。请小心,尽管我目前不确定这种方法是否有任何副作用。不过,我想这就是你要找的。
我还将更新 blog post 以反映更优雅、通用的代码。
package www.ourshopee.com.utils;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.RequiresApi;
import android.util.Log;
import java.util.Locale;
public class LocaleHelper {
private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";
public static void onCreate(Context context) {
String lang = getPersistedData(context, Locale.getDefault().getLanguage());
setLocale(context, lang);
}
public static void onCreate(Context context, String defaultLanguage) {
String lang = getPersistedData(context, defaultLanguage);
setLocale(context, lang);
}
public static String getLanguage(Context context) {
return getPersistedData(context, Locale.getDefault().getLanguage());
}
public static void setLocale(Context context, String language) {
persist(context, language);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
updateResources(context, language);
}
updateResourcesLegacy(context, language);
}
private static String getPersistedData(Context context, String defaultLanguage) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
}
private static void persist(Context context, String language) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(SELECTED_LANGUAGE, language);
editor.apply();
}
// private static void updateResources(Context context, String language) {
//
//
//
//
//
// Locale locale = new Locale(language);
// Locale.setDefault(locale);
// Configuration config = context.getResources().getConfiguration();
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// config.setLocale(locale);
// }
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// context.createConfigurationContext(config);
// }
// context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
//
//
//
//
//
//
// }
@TargetApi(Build.VERSION_CODES.N)
private static Context updateResources(Context context, String language) {
Locale locale = new Locale(language);
Log.d("LocaleHelper", "language above 24: "+language);
Locale.setDefault(locale);
Configuration configuration = context.getResources().getConfiguration();
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
return context.createConfigurationContext(configuration);
}
@SuppressWarnings("deprecation")
private static Context updateResourcesLegacy(Context context, String language) {
Locale locale = new Locale(language);
Log.d("LocaleHelper", "language below 24: "+language);
Locale.setDefault(locale);
Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
configuration.locale = locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLayoutDirection(locale);
}
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
return context;
}
}