资源和布局方向仅在Android 8.0及更高版本上呈现不正确

时间:2018-09-05 14:46:01

标签: android android-layout locale android-8.0-oreo

我有一个多语言的应用程序,其主要语言为英语,第二语言为阿拉伯语。

我正在应用程序中每个setLocale()的{​​{1}}中调用onCreate()

Activity

其中public static void setLocale(Locale locale){ Locale.setDefault(locale); Context context = MyApplication.getInstance(); final Resources resources = context.getResources(); final Configuration config = resources.getConfiguration(); config.setLocale(locale); context.getResources().updateConfiguration(config, resources.getDisplayMetrics()); } 是以下之一:

![enter image description here

上述方法在 locale被调用之前被称为。

documentation中所述,

  • 我已在清单中添加了super.onCreate(savedInstanceState)
  • 我已将所有具有android:supportsRtl="true"left属性的xml属性分别更改为rightstart
  • 我已将阿拉伯语言字符串放在end文件夹中,并将可绘制资源放在res\values-ar\strings文件夹中(其他资源也是如此)。

以上设置正常运行。将res\drawable-ar更改为Locale后,阿拉伯文字和资源会正确显示在我的活动中。

但是,所有 8.0 及更高版本的Android设备的资源和布局方向都存在问题。

在版本低于8.0的设备上,RTL屏幕 正确 如下所示:

enter image description here

在所有8.0+的设备上,出现的同一个屏幕如下所示:

enter image description here

错误

  

事实证明,方向和资源都在增加   显示不正确。

这里有两个问题:

  • 正确的ar-AE似乎并未在整个应用程序配置中更新。
  • 文本和可绘制对象的方向与应有的方向相反。

关于方向,存在一种我以前没有注意到的名为setLayoutDirection()的奇怪方法。

我想知道这个问题是什么,为什么它在奥利奥(Oreo)中发生,并且有什么解决方案。请对此提供帮助/发表评论。

编辑

  

根据API Differences report,   updateConfiguration()   该方法确实在Android 7.1(API级别25)中已弃用。

还找到了与此相关的所有帖子。按照重要性顺序:

1。 Android N change language programmatically

2。 Android context.getResources.updateConfiguration() deprecated

3。 How to change Android O / Oreo / api 26 app language

4。 Android RTL issue in API 24 and higher on locale change

5。 Change language programmatically (Android N 7.0 - API 24)

6。 Android N - Change Locale in runtime

7。 RTL layout bug in android Oreo

6 个答案:

答案 0 :(得分:4)

不推荐使用updateConfiguration()方法

现在我们需要使用createConfigurationContext()

我已经这样管理了

  

创建一个新的课程 ContextWrapper

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;

import java.util.Locale;

public class ContextWrapper extends android.content.ContextWrapper {

    public ContextWrapper(Context base) {
        super(base);
    }

    public static ContextWrapper wrap(Context context, Locale newLocale) {

        Resources res = context.getResources();
        Configuration configuration = res.getConfiguration();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(newLocale);

            LocaleList localeList = new LocaleList(newLocale);
            LocaleList.setDefault(localeList);
            configuration.setLocales(localeList);

            context = context.createConfigurationContext(configuration);

        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLocale(newLocale);
            context = context.createConfigurationContext(configuration);

        } else {
            configuration.locale = newLocale;
            res.updateConfiguration(configuration, res.getDisplayMetrics());
        }

        return new ContextWrapper(context);
    }}
  

创建一个新的类, BaseActivity

import android.content.Context;

import android.support.v7.app.AppCompatActivity;

import java.util.Locale;

/**
 * Created by nilesh on 20/3/18.
 */

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void attachBaseContext(Context newBase) {

        Locale newLocale;

        String lang = new PrefManager(newBase).getLanguage();

        if (lang.equals("zh_CN")) {
            newLocale = new Locale("zh");
        } else {
            newLocale = new Locale(lang);
        }


        Context context = ContextWrapper.wrap(newBase, newLocale);
        super.attachBaseContext(context);
    }
}
  

创建一个PrefManager类来存储语言环境

import android.content.Context;
import android.content.SharedPreferences;

public class PrefManager {

    private SharedPreferences.Editor editor;
    private Context mContext;
    private SharedPreferences prefs;
    private final String LANGUAGE = "language";
    private final String PREF = "user_data";

    public PrefManager(Context mContext) {
        this.mContext = mContext;
    }

    public String getLanguage() {
        this.prefs = this.mContext.getSharedPreferences(PREF, 0);
        return this.prefs.getString(LANGUAGE, "en_US");
    }

    public void setLanguage(String language) {
        this.editor = this.mContext.getSharedPreferences(PREF, 0).edit();
        this.editor.putString(LANGUAGE, language);
        this.editor.apply();
    }

}
  

现在,您需要在所有活动中扩展BaseActivity,例如

public class OrdersActivity extends BaseActivity
  

现在,当您需要更改Locale时,只需更新PrefManager中的值并重新开始活动

    PrefManager prefManager= new PrefManager(this);
    prefManager.setLanguage("zh_CN");
    //  restart your activity

答案 1 :(得分:1)

在API级别25中不推荐使用方法Resources#updateConfiguration (Configuration config, DisplayMetrics metrics)

医生建议使用Context#createConfigurationContext (Configuration overrideConfiguration)


您可以简单地创建一个基本活动,它是所有活动的共同父项,如下所示。

public class BaseActivity
        extends AppCompatActivity {

    private static final String LANGUAGE_CODE_ENGLISH = "en";
    private static final String LANGUAGE_CODE_ARABIC = "ar";

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(CommonUtils.getLanguageAwareContext(newBase));
    }

    private static Context getLanguageAwareContext(Context context) {
        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(new Locale(getLanguageCode));
        return context.createConfigurationContext(configuration);
    }

    // Rewrite this method according to your needs
    private static String getLanguageCode() {
        return LANGUAGE_CODE_ARABIC;
    }
}


注释

  • getLanguageCode()应该返回语言代码。通常,语言代码或代表它的任何其他数据都存储在首选项中。
  • 要动态更改语言,请在将适当的语言代码设置为首选项后重新创建活动。
  • 使用活动上下文而不是应用程序上下文来访问任何特定于语言环境的资源。换句话说,从活动中使用thisActivityName.this,从片段中使用getActivity(),而不是getApplicationContext()

答案 2 :(得分:1)

此问题的完整解决方案包括三个步骤:

步骤1

onCreate()(或所有BaseActivity)的Activity中,如下设置Locale

@Override
protected void onCreate(Bundle savedInstanceState) {

    // set the Locale the very first thing
    Utils.setLocale(Utils.getSavedLocale());
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    super.onCreate(savedInstanceState);

    ......
    ......

}

其中getSavedLocale()是与当前区域相对应的Locale(这将特定于您的项目...)。

方法Utils.setLocale(...)的定义如下:

public static void setLocale(Locale locale){
    Context context = MyApplication.getInstance();
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    Locale.setDefault(locale);
    configuration.setLocale(locale);
    configuration.setLayoutDirection(locale);

    // updateConfiguration(...) is deprecated in N
    if (Build.VERSION.SDK_INT >= 25) {
        context = context.getApplicationContext().createConfigurationContext(configuration);
        context = context.createConfigurationContext(configuration);
    }

    context.getResources().updateConfiguration(configuration,
            resources.getDisplayMetrics());
}

这将在每个Locale中设置正确的Activity。这对于支持API级别25的应用程序就足够了。对于API级别26和更高版本,还需要STEP 2和STEP 3。

第2步

在您的BaseActivity中覆盖以下方法:

@Override
protected void attachBaseContext(Context newBase) {
    newBase = Utils.getLanguageAwareContext(newBase);
    super.attachBaseContext(newBase);
}

其中函数getLanguageAwareContext(...)的定义如下:

public static Context getLanguageAwareContext(Context context){
    Configuration configuration = context.getResources().getConfiguration();
    Locale locale = getIntendedLocale();
    configuration.setLocale(locale);
    configuration.setLayoutDirection(locale);
    return context.createConfigurationContext(configuration);
}

这与STEP 1一起,在API级别26及以上的应用程序的每个Locale中设置了正确的Activity

但是,还需要一步来正确设置语言方向...

第3步

在您的onCreate()的{​​{1}}中,添加以下代码:

BaseActivity

其中@Override protected void onCreate(Bundle savedInstanceState) { .... .... // yup, it's a legit bug ... :) if (Build.VERSION.SDK_INT >= 26) { getWindow().getDecorView().setLayoutDirection(Utils.isRTL() ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); } .... .... } 函数的定义如下:

isRTL()

上述步骤应解决所有现有Android版本上的所有问题(至少与设置public static boolean isRTL(){ return TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL; } 和文本方向有关)。

答案 3 :(得分:0)

public void setLocale(final Context ctx, final String lang) {
    AppSettings.getInstance(ctx).save(PrefKeys.language, lang);
    final Locale loc = new Locale(lang);
    Locale.setDefault(loc);
    final Configuration cfg = new Configuration();
    cfg.locale = loc;
    ctx.getResources().updateConfiguration(cfg, null);
}

更改为英语:setLocale(getActivity(), "en";

更改为阿拉伯语:setLocale(getActivity(), "ar");

此后,您需要重新启动应用程序以获取语言更改效果。

答案 4 :(得分:0)

不推荐使用Resource.updateConfiguration,而是使用它:

 fun setLocale(old: Context, locale: Locale): Context {
    val oldConfig = old.resources.configuration
    oldConfig.setLocale(locale)
    return old.createConfigurationContext(oldConfig)
}

override fun attachBaseContext(newBase: Context?) {
    super.attachBaseContext(newBase?.let { setLocale(it, Locale("ar")) })
}

在Java中

private Context setLocale(Context old, Locale locale) {
    Configuration oldConfig = old.getResources().getConfiguration();
    oldConfig.setLocale(locale);
    return old.createConfigurationContext(oldConfig);
}

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(setLocale(newBase, new Locale("ar")));
}

答案 5 :(得分:0)

完全关闭了该应用程序,因为我认为它正在后台缓存。

使用下面的代码来说明我是如何实现的,您也可以尝试一下:

 Intent mStartActivity = new Intent(ctc, SplashActivity.class);
                int mPendingIntentId = 123456;
                PendingIntent mPendingIntent = PendingIntent.getActivity(ctc, mPendingIntentId,    mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
                AlarmManager mgr = (AlarmManager)ctc.getSystemService(Context.ALARM_SERVICE);
                mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
                System.exit(0);