多言語をサポートする古いプロジェクトがあります。サポートライブラリとターゲットプラットフォームをアップグレードしたい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
が機能しないことに気付きました。次のコードでも機能せず、デフォルトのリソースstring.xmlに常に_app_name
_ vlaueが表示されます。
_<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を返します。
最後に、私は自分のアプリに問題を見つけました。プロジェクトを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 Repository にあります。他のライブラリも最新の安定バージョンに変更します。
今私のアプリの依存関係セクションは次のようです:
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'
}
基本的に、バックグラウンドで起こっていることは、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) {
...
}
_
Chris Banesによる未リリースのコミット これは実際に修正されました:新しい設定は、ベースコンテキストの設定のディープコピーです。
_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
の下にこれを追加します。
Kotlinソリューション
_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"
_を指定しない場合にのみ機能しますあなたのマニフェスト。もしそうなら、さらに別のバグがあります:onConfigurationChanged
の内部では_newConfig.uiMode
_はAppCompatDelegateImpl
のonConfigurationChanged
によって設定されません。現在の夜間モードを計算するためにAppCompatDelegateImpl
が使用するすべてのコードをベースアクティビティコードにコピーし、_super.onConfigurationChanged
_呼び出しの前にそれをオーバーライドする場合も、これを修正できます。 Kotlinでは、次のようになります。
_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)
}
_
12月16日更新:
_AppCompatDelegateImpl.Java
_ の最近のバージョンは、この投稿を最初に作成してから、さらに多くの変更を受けています。上記で述べたChris Banesによる修正は、configChanges
に関連するすべてのバグを完全に取り除くには十分ではなかったようです(_configChanges="uiMode"
_の上記の回避策を確認でき、コメントで1人のユーザーが指摘しました)ここでは、彼の場合、orientation
の値はonConfigurationChanged
で変化していませんでした。最新の実装では、代わりにgenerateConfigDelta
と呼ばれる新しいメソッドに依存して正しい構成インスタンスを作成しているため、ナイトモードを処理する他のすべての方法は効果的に非推奨になっています。
個人的に、私は何ヶ月も問題なく、またはユーザーからの不満なしに私のソリューションを使用してきましたが、AppCompatDelegateImpl
はまだ進行中の作業であることを認識しておく必要があります。したがって、私の判断で私のソリューションを使用してください。また、以前のライブラリバージョンへのダウングレードを示唆する他の回答も確認してください可能性がありますあなたの状況でより適切である。
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);
}
最後に、ロケートの解決策を得ました。私の場合、実際にはロケートファイルを分割するため、問題は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
}
}
このコードをAndroidブロックbuild.gradle
ファイルに追加すると、問題が解決します。
今も動作する新しいバージョンがあります:
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
@Fredが述べたようにappcompat:1.1.0-alpha03
には問題がありますが、 リリースバージョンのログには記載されていません
androidx.appcompat:appcompat:1.1.0
にも同じバグがありました。 androidx.appcompat:appcompat:1.1.0-rc01
に切り替えて、Android 5-6.
の言語が変更されました