gpt4 book ai didi

android - 迁移到 Androidx 后更改区域设置不起作用

转载 作者:塔克拉玛干 更新时间:2023-11-02 09:07:32 40 4
gpt4 key购买 nike

我有一个支持多语言的旧项目。我要升级支持库和目标平台,在迁移到 Androidx 之前一切正常,但现在更改语言不起作用!

我使用此代码更改 App 的默认语言环境

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 在每个 Activity 上调用此方法像这样:
@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默认资源 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!

最佳答案

2020 年 8 月 21 日更新:
AppCompat 1.2.0 终于发布了。如果您不使用 ContextWrapperContextThemeWrapper根本没有其他事情可做,您应该能够从 1.1.0 中删除任何解决方法!
如果您确实使用 ContextWrapperContextThemeWrapper里面 attachBaseContext , 语言环境更改将中断,因为当您将包装的上下文传递给 super 时,

  • 1.2.0 AppCompatActivity进行内部调用,将您的 ContextWrapper 包装起来在另一个 ContextThemeWrapper ,
  • 或者如果您使用 ContextThemeWrapper ,将其配置覆盖为空白,类似于 1.1.0 中发生的情况。

  • 但解决方案总是一样的。对于情况 2,我尝试了多种其他解决方案,但正如 @Kreiri 在评论中指出的那样(感谢您的调查帮助!), AppCompatDelegateImpl总是以剥离语言环境而告终。最大的障碍是,与 1.1.0 不同, applyOverrideConfiguration在您的基本上下文而不是您的主机 Activity 上调用,因此您不能像在 1.1.0 中那样在 Activity 中覆盖该方法并修复语言环境。我知道的唯一可行的解​​决方案是通过覆盖 getDelegate() 来反转包装。确保您的包装和/或语言环境覆盖在最后。首先,添加下面的类:
    Kotlin 示例(请注意,该类必须位于 androidx.appcompat.app 包内,因为唯一现有的 AppCompatDelegate 构造函数是包私有(private)的)
    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")
    }
    }
    然后在我们的基本 Activity 类中删除所有 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) {
    ...
    }
    In an unreleased commit by 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",这只会自行起作用在你的 list 里面。如果你这样做了,那么还有另一个错误:内部 onConfigurationChanged newConfig.uiMode不会被 AppCompatDelegateImpl 设置的 onConfigurationChanged .如果您复制所有代码 AppCompatDelegateImpl,也可以修复此问题。用于计算当前夜间模式到您的基本 Activity 代码,然后在 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)
    }

    关于android - 迁移到 Androidx 后更改区域设置不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55265834/

    40 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com