У меня есть старый проект, который поддерживает несколько языков. Я хочу обновить библиотеку поддержки и целевую платформу, До перехода на Androidx все работало нормально, но теперь не работает смена языка!
Я использую этот код, чтобы изменить язык приложения по умолчанию.
private static Context updateResources(Context context, String language)
{
Locale locale = new Locale(language);
Locale.setDefault(locale);
Configuration configuration = context.getResources().getConfiguration();
configuration.setLocale(locale);
return context.createConfigurationContext(configuration);
}
И вызовите этот метод для каждого действия путем переопределения attachBaseContext следующим образом:
@Override
protected void attachBaseContext(Context newBase)
{
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String language = preferences.getString(SELECTED_LANGUAGE, "fa");
super.attachBaseContext(updateResources(newBase, language));
}
Я попробовал другой метод, чтобы получить строку, и заметил, что getActivity().getBaseContext().getString работает, а getActivity().getString не работает. Даже следующий код не работает и всегда показывает app_name vlaue в ресурсе по умолчанию string.xml.
<TextView
android:id = "@+id/textView"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "@string/app_name"/>
Я делюсь примером кода в https://github.com/Freydoonk/LanguageTest
Также getActivity()..getResources().getIdentifier не работает и всегда возвращает 0!
Вы воссоздаете действие после изменения языка?
@ mustafiz012 mustafiz012 Если я вызову recareate в attachBaseContext, сделайте циклический вызов и ...
@IbrahimAli Я делюсь примером кода в github.com/Freydoonk/LanguageTest
Интересно, что установка android:configChanges = "uiMode" решает проблему, но вместо этого, естественно, мы получаем цветовые артефакты при переключении между светлой и темной темой...
1. Метод, который вы можете использовать в прикрепитьBaseContext()
private void setLanguage(Context mContext, String localeName) {
Locale myLocale = new Locale(localeName);
Resources res = mContext.getResources();
DisplayMetrics dm = res.getDisplayMetrics();
Configuration conf = res.getConfiguration();
conf.locale = myLocale;
res.updateConfiguration(conf, dm);
}
2. Переопределение действий
@Override
protected void attachBaseContext(Context newBase) {
setLanguage(newBase, "your language");
super.attachBaseContext(newBase);
}
Примечание: У меня это работает нормально после воссоздания активности
Где вы вызываете «воссоздать();»
Предположим, вы используете кнопку, чтобы изменить язык. В этом случае, когда вы нажмете кнопку, поместите свой язык в общие настройки и воссоздайте свою активность. Получите языковую строку из общих настроек, когда это необходимо. Если вам нужна дополнительная информация, я поделюсь примером кода.
Я делаю это, я сохраняю выбранный язык в общих настройках и воссоздаю приложение, я получаю выбранный язык из общих настроек и перехожу к setLanguage(newBase, myPpersistedLanguage); в attachBaseContext, но это не работает. Я редактирую полный attachBaseContext в примере кода в своем вопросе, чтобы показать это.
Я думаю, что ваш файл strings.xml настроен не так, как требуется. Скажите, пожалуйста, у вас есть альтернативный файл strings.xml?? Например: strings.xml, strings.xml (fa)
Добавляю скриншот своего ресурса в свой проект
Затем я поделюсь своим рабочим примером кода. Пожалуйста, дайте мне немного времени.
Давайте продолжить обсуждение в чате.
Наконец, я нахожу проблему в своем приложении. При переносе проекта на Androidx зависимости моего проекта изменились следующим образом:
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.1.0-alpha03'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.1.0-alpha04'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
}
Как видно, версия androidx.appcompat:appcompat равна 1.1.0-alpha03, когда я изменил ее на последнюю стабильную версию, 1.0.2, моя проблема решена, и смена языка работает правильно.
Я нашел последнюю стабильную версию библиотеки appcompat в Репозиторий Maven. Я также меняю другие библиотеки на последнюю стабильную версию.
Теперь раздел зависимостей моего приложения выглядит следующим образом:
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.0.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
У меня была аналогичная проблема. У меня уже был стабильный 1.0.2 для appcompat, но моя проблема была в библиотеке поддержки дизайна или com.google.android.material:material:1.1.0-alpha06. Поэтому, когда я перешел на стабильную версию 1.0.0, она была исправлена.
Теперь есть более новая версия, которая также работает:
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
Как упомянул @Fred, у appcompat:1.1.0-alpha03 есть сбой, хотя он не упоминается в их журнал выпусков версий.
Тестирую androidx.appcompat:appcompat:1.1.0-alpha04, у него пока такая проблема.
для моего производственного приложения работает просто отлично, я использую собственную реализацию для ContextWrapper внутри attachBaseContext(Context newBase) для каждого действия
Наконец-то я получил решение для локации. В моем случае на самом деле проблема была с bundle apk, потому что она разделяла файлы локации. В bundle apk по умолчанию будут генерироваться все сплиты. но в блоке android вашего файла build.gradle вы можете объявить, какие разбиения будут сгенерированы.
bundle {
language {
// Specifies that the app bundle should not support
// configuration APKs for language resources. These
// resources are instead packaged with each base and
// dynamic feature APK.
enableSplit = false
}
}
После добавления этого кода в файл андроид блок build.gradle моя проблема была решена.
У меня была такая же проблема. Когда я проверял перед публикацией в плеймаркете, все работало, а после публикации смена локали не работала. Я применил это решение и теперь работает безупречно. Спасибо
У меня такая же проблема. Всякий раз, когда я устанавливаю отладочную версию, она работает, но когда я выпускаю пакет, возникает проблема, и я пробовал много решений, но это единственное, что сработало. Спасибо.
Этот! Это имеет большой смысл. Один из тестируемых телефонов никогда не отображал выбранный язык из приложения, и мы задались вопросом, почему это происходит только при загрузке из Google Play. Я проверил его работу, добавив системный язык -> очистить данные -> перезапустив приложение. Спасибо!
Была такая же ошибка на androidx.appcompat:appcompat:1.1.0. Перешел на androidx.appcompat:appcompat:1.1.0-rc01 и теперь языки меняются на Android 5-6.
В новых библиотеках совместимости приложений есть проблема, связанная с ночным режимом, которая приводит к переопределению конфигурации на Android с 21 по 25. Это можно исправить, применив вашу конфигурацию при вызове этой общедоступной функции:
public void applyOverrideConfiguration(Configuration overrideConfiguration
Для меня этот небольшой трюк сработал, скопировав настройки из переопределенной конфигурации в мою конфигурацию, но вы можете делать все, что хотите. Лучше повторно применить языковую логику к новой конфигурации, чтобы свести к минимуму ошибки.
@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
if (Build.VERSION.SDK_INT >= 21&& Build.VERSION.SDK_INT <= 25) {
//Use you logic to update overrideConfiguration locale
Locale locale = getLocale()//your own implementation here;
overrideConfiguration.setLocale(locale);
}
super.applyOverrideConfiguration(overrideConfiguration);
}
это изменение кода приводит к сбою в нескольких местах на некоторых устройствах Attempt to invoke virtual method 'java.lang.String java.util.Locale.toString()' on a null object reference android.widget.Editor$TextActionModeCallback.populateMenuWithItemsExt. Лучше посоветовать перейти на более раннюю версию или следовать ответу @ 0101100101.
getLocale и setLocale должны быть на Android N/24 или выше
ОБНОВЛЕНИЕ 21 августа 2020 г.:
Наконец-то был выпущен AppCompat 1.2.0. Если вы вообще не используете ContextWrapper или ContextThemeWrapper, вам больше нечего делать, и вы сможете удалить все обходные пути, которые были у вас в 1.1.0!
Если вы ДЕЙСТВИТЕЛЬНО используете ContextWrapper или ContextThemeWrapper внутри attachBaseContext, изменения локали сломаются, потому что, когда вы передаете обернутый контекст в super,
AppCompatActivity выполняет внутренние вызовы, которые заключают ваш ContextWrapper в другой ContextThemeWrapper,ContextThemeWrapper, заменяет его конфигурацию пустой, аналогично тому, что произошло в 1.1.0.Но решение всегда одно. Я пробовал несколько других решений для ситуации 2, но, как указал @Kreiri в комментариях (спасибо за вашу помощь в расследовании!), AppCompatDelegateImpl всегда заканчивался удалением локали. Большим препятствием является то, что, в отличие от 1.1.0, applyOverrideConfiguration вызывается в вашем базовом контексте, а не в вашей активности хоста, поэтому вы не можете просто переопределить этот метод в своей активности и исправить локаль, как вы могли в 1.1.0. Единственное рабочее решение, о котором я знаю, это измените обертку, переопределив getDelegate(), чтобы убедиться, что ваша обертка и / или переопределение локали будут последними. Сначала вы добавляете класс ниже:
Образец Kotlin (обратите внимание, что класс ДОЛЖЕН находиться внутри пакета androidx.appcompat.app, потому что единственный существующий конструктор AppCompatDelegate является частным пакетом)
package androidx.appcompat.app
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
import android.util.AttributeSet
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.Toolbar
class BaseContextWrappingDelegate(private val superDelegate: AppCompatDelegate) : AppCompatDelegate() {
override fun getSupportActionBar() = superDelegate.supportActionBar
override fun setSupportActionBar(toolbar: Toolbar?) = superDelegate.setSupportActionBar(toolbar)
override fun getMenuInflater(): MenuInflater? = superDelegate.menuInflater
override fun onCreate(savedInstanceState: Bundle?) {
superDelegate.onCreate(savedInstanceState)
removeActivityDelegate(superDelegate)
addActiveDelegate(this)
}
override fun onPostCreate(savedInstanceState: Bundle?) = superDelegate.onPostCreate(savedInstanceState)
override fun onConfigurationChanged(newConfig: Configuration?) = superDelegate.onConfigurationChanged(newConfig)
override fun onStart() = superDelegate.onStart()
override fun onStop() = superDelegate.onStop()
override fun onPostResume() = superDelegate.onPostResume()
override fun setTheme(themeResId: Int) = superDelegate.setTheme(themeResId)
override fun <T : View?> findViewById(id: Int) = superDelegate.findViewById<T>(id)
override fun setContentView(v: View?) = superDelegate.setContentView(v)
override fun setContentView(resId: Int) = superDelegate.setContentView(resId)
override fun setContentView(v: View?, lp: ViewGroup.LayoutParams?) = superDelegate.setContentView(v, lp)
override fun addContentView(v: View?, lp: ViewGroup.LayoutParams?) = superDelegate.addContentView(v, lp)
override fun attachBaseContext2(context: Context) = wrap(superDelegate.attachBaseContext2(super.attachBaseContext2(context)))
override fun setTitle(title: CharSequence?) = superDelegate.setTitle(title)
override fun invalidateOptionsMenu() = superDelegate.invalidateOptionsMenu()
override fun onDestroy() {
superDelegate.onDestroy()
removeActivityDelegate(this)
}
override fun getDrawerToggleDelegate() = superDelegate.drawerToggleDelegate
override fun requestWindowFeature(featureId: Int) = superDelegate.requestWindowFeature(featureId)
override fun hasWindowFeature(featureId: Int) = superDelegate.hasWindowFeature(featureId)
override fun startSupportActionMode(callback: ActionMode.Callback) = superDelegate.startSupportActionMode(callback)
override fun installViewFactory() = superDelegate.installViewFactory()
override fun createView(parent: View?, name: String?, context: Context, attrs: AttributeSet): View? = superDelegate.createView(parent, name, context, attrs)
override fun setHandleNativeActionModesEnabled(enabled: Boolean) {
superDelegate.isHandleNativeActionModesEnabled = enabled
}
override fun isHandleNativeActionModesEnabled() = superDelegate.isHandleNativeActionModesEnabled
override fun onSaveInstanceState(outState: Bundle?) = superDelegate.onSaveInstanceState(outState)
override fun applyDayNight() = superDelegate.applyDayNight()
override fun setLocalNightMode(mode: Int) {
superDelegate.localNightMode = mode
}
override fun getLocalNightMode() = superDelegate.localNightMode
private fun wrap(context: Context): Context {
TODO("your wrapping implementation here")
}
}
Затем внутри нашего базового класса активности вы удаляете все свои обходные пути 1.1.0 и просто добавляете это:
private var baseContextWrappingDelegate: AppCompatDelegate? = null
override fun getDelegate() = baseContextWrappingDelegate ?: BaseContextWrappingDelegate(super.getDelegate()).apply {
baseContextWrappingDelegate = this
}
В зависимости от используемой вами реализации ContextWrapper изменения конфигурации могут нарушить изменение тем или языковых стандартов. Чтобы исправить это, дополнительно добавьте это:
override fun createConfigurationContext(overrideConfiguration: Configuration) : Context {
val context = super.createConfigurationContext(overrideConfiguration)
TODO("your wrapping implementation here")
}
И ты хорош! Вы можете ожидать, что Google снова сломает это в 1.3.0. Я буду там, чтобы исправить это ... До встречи, космический ковбой!
СТАРЫЙ ОТВЕТ И РЕШЕНИЕ ДЛЯ APPCOMPAT 1.1.0:
В основном то, что происходит в фоновом режиме, заключается в том, что, хотя вы правильно установили конфигурацию в attachBaseContext, AppCompatDelegateImpl затем переходит и переопределяет конфигурацию на полностью свежая конфигурация без локали:
final Configuration conf = new Configuration();
conf.uiMode = newNightMode | (conf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
try {
...
((android.view.ContextThemeWrapper) mHost).applyOverrideConfiguration(conf);
handled = true;
} catch (IllegalStateException e) {
...
}
В неопубликованном коммите Криса Бейнса это было фактически исправлено: новая конфигурация является глубокой копией конфигурации базового контекста.
final Configuration conf = new Configuration(baseConfiguration);
conf.uiMode = newNightMode | (conf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
try {
...
((android.view.ContextThemeWrapper) mHost).applyOverrideConfiguration(conf);
handled = true;
} catch (IllegalStateException e) {
...
}
Пока это не будет выпущено, можно сделать то же самое вручную. Чтобы продолжить использовать версию 1.1.0, добавьте это под своим attachBaseContext:
Котлин решение
override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) {
if (overrideConfiguration != null) {
val uiMode = overrideConfiguration.uiMode
overrideConfiguration.setTo(baseContext.resources.configuration)
overrideConfiguration.uiMode = uiMode
}
super.applyOverrideConfiguration(overrideConfiguration)
}
Java-решение
@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
if (overrideConfiguration != null) {
int uiMode = overrideConfiguration.uiMode;
overrideConfiguration.setTo(getBaseContext().getResources().getConfiguration());
overrideConfiguration.uiMode = uiMode;
}
super.applyOverrideConfiguration(overrideConfiguration);
}
Этот код делает то же самое, что Configuration(baseConfiguration) делает под капотом, но поскольку мы делаем это после, AppCompatDelegate уже установил правильное uiMode, мы должны убедиться, что переопределенное uiMode перешло к после того, как мы его исправим, чтобы мы не потерять настройку темного/светлого режима.
Пожалуйста, обрати внимание, что это работает само по себе, только если вы не укажете configChanges = "uiMode" внутри своего манифеста. Если вы это сделаете, то есть еще одна ошибка: внутри onConfigurationChangednewConfig.uiMode не будет установлен AppCompatDelegateImplonConfigurationChanged. Это также можно исправить, если вы скопируете весь код, который AppCompatDelegateImpl использует для расчета текущего ночного режима, в свой базовый код активности, а затем переопределите его перед вызовом super.onConfigurationChanged. В Котлине это будет выглядеть так:
private var activityHandlesUiMode = false
private var activityHandlesUiModeChecked = false
private val isActivityManifestHandlingUiMode: Boolean
get() {
if (!activityHandlesUiModeChecked) {
val pm = packageManager ?: return false
activityHandlesUiMode = try {
val info = pm.getActivityInfo(ComponentName(this, javaClass), 0)
info.configChanges and ActivityInfo.CONFIG_UI_MODE != 0
} catch (e: PackageManager.NameNotFoundException) {
false
}
}
activityHandlesUiModeChecked = true
return activityHandlesUiMode
}
override fun onConfigurationChanged(newConfig: Configuration) {
if (isActivityManifestHandlingUiMode) {
val nightMode = if (delegate.localNightMode != AppCompatDelegate.MODE_NIGHT_UNSPECIFIED)
delegate.localNightMode
else
AppCompatDelegate.getDefaultNightMode()
val configNightMode = when (nightMode) {
AppCompatDelegate.MODE_NIGHT_YES -> Configuration.UI_MODE_NIGHT_YES
AppCompatDelegate.MODE_NIGHT_NO -> Configuration.UI_MODE_NIGHT_NO
else -> applicationContext.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
}
newConfig.uiMode = configNightMode or (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK.inv())
}
super.onConfigurationChanged(newConfig)
}
Это сработало идеально для меня. Спасибо! Но я не понял, как переопределение метода «applyOverrideConfiguration» решает проблему. Не могли бы вы объяснить это?
@Burak Если вы проверите исходный код ContextThemeWrapper, вы сможете увидеть, что все, что передается в super.applyOverrideConfiguration(overrideConfiguration), сохраняется и позже используется для извлечения любых ресурсов. Именно так Google реализовал тему день/ночь. Базовая конфигурация ресурса не имеет правильной настройки uiMode, поэтому ContextThemeWrapper переопределяет getResources(), чтобы вернуть экземпляр, в котором он есть в конфигурации.
Это решение имеет проблемы, если приложение поддерживает альбомную ориентацию.
@phongvan какие проблемы? Я использую это в течение долгого времени, и у меня никогда не было проблем с пейзажем или переключением между портретом и пейзажем. Почти уверен, что если вы столкнетесь с какими-либо сбоями, это связано с другими ошибками в версии 1.1.0, а не с моим решением, потому что, как объяснено в ответе, оно напрямую получено из официального исправления Google.
@0101100101 Когда я использую этот код, я возвращаюсь к экрану, ориентация не меняется при onconfigchange
@phongvan Я заметил, что Google много работает над реализацией, и вполне возможно, что такие ошибки могут возникнуть. Смотрите обновление в моем ответе. Также кажется, что то, что вы описываете, может происходить совершенно случайным образом, иногда все работает нормально, а иногда ломается.
этот applyOverrideConfiguration обходной путь работает для меня. Без этой локали не работает Android 6.0. Я использую androidx.appcompat:appcompat:1.1.0
Не понимаю, у меня не работает. Я поместил applyOverrideConfiguration в свой MainActivity. Затем у меня есть метод updateResource в моем помощнике. Затем в моем UITest внутри @Before я позвонил: updateResources(InstrumentationRegistry.getInstrumentation().context, «EN»), но не работает, помогите, пожалуйста? NB: версия «androidx.appcompat: appcompat: 1.1.0»
@TaiNguyen, какой у вас метод updateResource, что он делает? Просто хочу отметить, что если вы используете ContextWrappers в attachBaseContext, для того, чтобы мое решение заработало, необходимы дополнительные изменения кода, поскольку у ContextWrappers может быть своя собственная логика, например, сохранение устаревших экземпляров Resources и Configuration.
Для меня этот дополнительный шаг (согласно комментарию чуть выше) был необходим... не передавать ContextWrapper в super.attachBaseContext(), а вместо этого просто передать Context, то есть без переноса. Это в сочетании с использованием applyOverrideConfiguration(), кажется, работает для меня. Спасибо. Но Зачем Google делает так жесткий, чтобы сделать что-то настолько просто??!!
ОБНОВЛЕНИЕ.... аааа, нет, он все еще не работает стабильно. По-прежнему иногда используется язык по умолчанию (системный). Андроид это кошмар. После выхода из моей активности, если я снова открою ее вскоре после этого, будет использоваться язык по умолчанию (системный). Если я открою список «последние приложения» и закрою все, а затем открою «Действие», будет использован правильный язык (переопределение вручную). Как будто какая-то конфигурация по умолчанию (системная) все еще используется.
дальнейшее ОБНОВЛЕНИЕ @ 0101100101 ... делая то, что вы предлагаете в этом ответе, плюс также понижение до appcompat 1.1.0, кажется, работает лучше для меня ... использовал appcompat 1.2.0-beta01. Меня беспокоит то, что appcompat развивается в направлении, которое сделает этот ответ излишним... есть идеи, куда сообщать об этих проблемах с appcompat?
последнее ОБНОВЛЕНИЕ... нашел что-то, что работает лучше для меня... см. мой ответ здесь: stackoverflow.com/a/61643167/4070848
@drmrbrewer «Меня беспокоит, что appcompat развивается в направлении, которое сделает этот ответ излишним», да, это было в значительной степени ожидаемо. Смотрите мое последнее обновление к этому ответу.
Использование ContextThemeWrapper вместо ContextWrapper не помогает.
@Kreiri, можете ли вы попробовать, если это работает, добавив в дополнение к этому решение applyOverrideConfiguration? Я только что отредактировал свой ответ, чтобы предложить это. Я заметил, что код AppCompatDelegateImpl все еще нарушает конфигурацию, это смешно. Дайте мне знать, как это происходит.
@ 0101100101 У меня не получилось. applyOverrideConfiguration не вызывается с 1.2.0 в моем проекте...
@Kreiri Я вижу, реализация на самом деле больше не вызывает applyOverrideConfiguration активность, а только базовый контекст. Смотрите обновленный ответ и решение: просто позвоните getResources(), прежде чем звонить super.attachBaseContext. Не нужно переопределять applyOverrideConfiguration.
@ 0101100101 Даже при вызове getResources() для нового контекста перед его передачей в super.attachBaseContext локаль в действии не меняется. attachBaseContext2 всегда возвращает контекст с языковым стандартом системы.
@Kreiri, вы правы, я только что проверил, и реализации Google еще удается убрать локаль, вздох ... Попробуйте удалить все обходные пути и переключиться на решение BaseContextWrappingDelegate, независимо от того, используете ли вы тему или обычную оболочку. Вам просто нужно переместить обертку и изменение локали из вашей активности внутрь этого класса.
Да, начиная с 1.2.0, applyOverrideConfiguration не вызывается. Вы должны вызвать эту функцию вручную из attachBaseContext
В случае, если это может быть кому-то полезно, здесь представляет собой полную разницу в рабочей реализации изменения локали, основанную на объединении этого ответа с Еще один с использованием действия AppCompatActivity и библиотеки версии 1.2.0.
спасибо за обновление appCompat 1.2.0, люблю вас.
Наконец нашел ответ, который работает для меня, я работал над этим несколько дней и не смог заставить его работать; это здорово. Большое спасибо за этот отличный ответ
Это также работает с AppCompat 1.3.0-beta01!
Ответ от @ 0101100101 у меня сработал.
Только то, что я использовал
@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration)
{
if (overrideConfiguration != null) {
int uiMode = overrideConfiguration.uiMode;
overrideConfiguration.setTo(getResources().getConfiguration());
overrideConfiguration.uiMode = uiMode;
}
super.applyOverrideConfiguration(overrideConfiguration);
}
так что только getResources() вместо getBaseContext().getResources().
В моем случае я расширил ContextWrapper с переопределенным getResources(). Но после вызова applyOverrideConfiguration я не могу получить доступ к своим пользовательским getResources. Вместо них я беру стандартные.
Если я использую код выше, все работает нормально.
Ошибка androidx.appcompat:appcompat:1.1.0 также может быть решена простым вызовом getResources() в Activity.applyOverrideConfiguration()
@Override public void
applyOverrideConfiguration(Configuration cfgOverride)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// add this to fix androidx.appcompat:appcompat 1.1.0 bug
// which happens on Android 6.x ~ 7.x
getResources();
}
super.applyOverrideConfiguration(cfgOverride);
}
Поздний ответ, но я подумал, что это может быть полезно. Начиная с androidx.appcompat:appcompat:1.2.0-beta01 Решение переопределения 0101100101applyOverrideConfiguration больше не работает на мне. Вместо этого в переопределенном attacheBaseContext вы должны вызвать applyOverrideConfiguration()без переопределения.
override fun attachBaseContext(newBase: Context) {
val newContext = LocaleHelper.getUpdatedContext(newBase)
super.attachBaseContext(newContext)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1){
applyOverrideConfiguration(newContext.resources.configuration)
}
}
Жаль только, что его решение работает только на 1.1.0. Основываясь на моих исследованиях, это должно было быть официально исправлено. Просто странно, что этот баг все еще здесь. Я знаю, что использую бета-версию, но для тех, кто хочет использовать последнюю версию, это решение работает. Протестировано на уровне API эмулятора 21-25. Выше этого уровня API вам не нужно об этом беспокоиться.
это единственное решение, которое сработало для меня для androidx.appcompat... 1.2.0, в противном случае изменение локали не происходило на некоторых телефонах (в моем случае - samsung s5). Спасибо!
Попробуйте что-то вроде этого:
public class MyActivity extends AppCompatActivity {
public static final float CUSTOM_FONT_SCALE = 4.24f;
public static final Locale CUSTOM_LOCALE = Locale.CANADA_FRENCH; // or whatever
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(useCustomConfig(newBase));
}
private Context useCustomConfig(Context context) {
Locale.setDefault(CUSTOM_LOCALE);
if (Build.VERSION.SDK_INT >= 17) {
Configuration config = new Configuration();
config.fontScale = CUSTOM_FONT_SCALE;
config.setLocale(CUSTOM_LOCALE);
return context.createConfigurationContext(config);
} else {
Resources res = context.getResources();
Configuration config = new Configuration(res.getConfiguration());
config.fontScale = CUSTOM_FONT_SCALE;
config.locale = CUSTOM_LOCALE;
res.updateConfiguration(config, res.getDisplayMetrics());
return context;
}
}
}
Источники: комментарий об ошибке и первый образец, связанный с комментарием о проблеме.
В то время как вышеизложенное работает нормально для меня, другой вариант из второй образец, связанный с комментарием о проблеме выглядит следующим образом (я лично не пробовал):
@RequiresApi(17)
public class MyActivity extends AppCompatActivity {
public static final float CUSTOM_FONT_SCALE = 4.24f;
public static final Locale CUSTOM_LOCALE = Locale.CANADA_FRENCH; // or whatever
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
Configuration config = new Configuration();
config.fontScale = CUSTOM_FONT_SCALE;
applyOverrideConfiguration(config);
}
@Override
public void applyOverrideConfiguration(Configuration newConfig) {
super.applyOverrideConfiguration(updateConfigurationIfSupported(newConfig));
}
private Configuration updateConfigurationIfSupported(Configuration config) {
if (Build.VERSION.SDK_INT >= 24) {
if (!config.getLocales().isEmpty()) {
return config;
}
} else {
if (config.locale != null) {
return config;
}
}
Locale locale = CUSTOM_LOCALE;
if (locale != null) {
if (Build.VERSION.SDK_INT >= 17) {
config.setLocale(locale);
} else {
config.locale = locale;
}
}
return config;
}
}
config.setLocale(locale); должен быть на Android N (24) или выше.
Я использую "androidx.appcompat:appcompat:1.3.0-alpha01", но я полагаю, что он также будет работать на Версия 1.2.0.
Следующий код основан на Поиск кода Android.
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import java.util.*
open class MyBaseActivity :AppCompatActivity(){
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase)
val config = Configuration()
applyOverrideConfiguration(config)
}
override fun applyOverrideConfiguration(newConfig: Configuration) {
super.applyOverrideConfiguration(updateConfigurationIfSupported(newConfig))
}
open fun updateConfigurationIfSupported(config: Configuration): Configuration? {
// Configuration.getLocales is added after 24 and Configuration.locale is deprecated in 24
if (Build.VERSION.SDK_INT >= 24) {
if (!config.locales.isEmpty) {
return config
}
} else {
if (config.locale != null) {
return config
}
}
// Please Get your language code from some storage like shared preferences
val languageCode = "fa"
val locale = Locale(languageCode)
if (locale != null) {
// Configuration.setLocale is added after 17 and Configuration.locale is deprecated
// after 24
if (Build.VERSION.SDK_INT >= 17) {
config.setLocale(locale)
} else {
config.locale = locale
}
}
return config
}
}
Я думаю, что это самый простой ответ, который решает мою проблему, спасибо! @EhsanShadi Я просто подожду обновлений 1.3.0, что тогда произойдет :)
Как этот работает? создание новой конфигурации переопределяет все предварительно определенные конфигурации и ломает пользовательский интерфейс.
@dor506 dor506 с какими поломками пользовательского интерфейса вы сталкиваетесь? Кажется, это единственное решение, которое сработало для меня androidx.appcompat:appcompat:1.3.0-alpha03, но я был бы рад узнать о некоторых предостережениях, которые необходимо подготовить к выпуску. Спасибо
@saintjab Взгляните на последнее обновление 0101100101 (20 августа), которое действительно работает при обновлении текущей конфигурации (context.getResources().getConfiguration()), вместо создания новой конфигурации в каждом действии.
Решение:
В Gradle уровня приложения я включил следующий код в подразделение Android:
bundle {
language {
// Specifies that the app bundle should not support
// configuration APKs for language resources. These
// resources are instead packaged with each base and
// dynamic feature APK.
enableSplit = false
}
}
https://medium.com/dwarsoft/how-to-provide-languages-dynamically-using-app-bundle-567d2ec32be6
Теперь язык не меняется с этими библиотеками: androidx.appcompat:appcompat:1.1.0, androidx.appcompat:appcompat:1.2.0
Проблема была решена только в этой библиотеке: androidx.appcompat:appcompat:1.3.0-rc01
Эй, хорошо ли использовать версию 1.3.0-rc01 вместо 1.2.0 в выпуске производственного уровня?
да это хорошо. В противном случае 1.2.0 вообще не работает. В моем случае были проблемы с контекстом приложения, но все было хорошо с контекстом действия.
Я думаю, что такие проблемы будет намного проще решить, если вы предоставите образец приложения.