web-dev-qa-db-ja.com

Android 8.0以上でのみリソースとレイアウト方向が正しくレンダリングされない

私は第一言語が英語、第二言語がアラビア語の多言語アプリを持っています。

アプリ内のすべてのActivitysetLocale()onCreate()を呼び出しています:

_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());
}
_

ここで、localeは次のいずれかです。

![enter image description here

上記のメソッドが呼び出される前にbeforesuper.onCreate(savedInstanceState)が呼び出されます。

documentation で説明されているように、

  • マニフェストに_Android:supportsRtl="true"_を追加しました。
  • leftおよびright属性を持つすべてのxmlプロパティを、それぞれstartおよびendに変更しました。
  • アラビア語の文字列を_res\values-ar\strings_フォルダーに入れ、ドローアブルリソースを_res\drawable-ar_フォルダーに入れました(他のリソースも同様です)。

上記の設定は正しく動作します。 Localeを_ar-AE_に変更すると、アクティビティにアラビア語のテキストとリソースが正しく表示されます。

ただし、すべてのAndroidバージョン8.0を搭載したデバイス)以降では、リソースとレイアウト方向の両方に問題があります。

バージョンが8.0より前のデバイスでは、RTL画面が正しくは次のようになります。

enter image description here

また、8.0以降のすべてのデバイスで、同じ画面が次のように表示されます。

enter image description here

これはwrongです。

方向とリソースの両方が正しく表示されていないことがわかります。

ここには2つの問題があります。

  • 正しいLocaleは、アプリ構成全体で更新されていないようです。
  • テキストとドローアブルの方向は、本来あるべき方向とは逆です。

方向に関しては、以前気づかなかった setLayoutDirection() と呼ばれる奇妙なメソッドが存在します。

この問題が何であるか、なぜそれがオレオで発生するのか、そしてその解決策は何ですか?これについて助け/コメントしてください。

[〜#〜]編集[〜#〜]

API Differencesレポート によると、 updateConfiguration() メソッドは実際にAndroid 7.1(API level 25) 。

また、これに関連するすべての投稿を見つけました。重要度の高い順に:

1。Android Nプログラムで言語を変更

2。Android context.getResources.updateConfiguration()deprecated

3。変更方法Android O/Oreo/api 26アプリの言語

4。ロケールの変更によるAPI 24以降のAndroid RTLの問題

5。プログラムで言語を変更します(Android N 7.0-API 24)

6。Android N-実行時にロケールを変更

7。RTLレイアウトのバグAndroid Oreo

16
Y.S

この問題の完全な解決策は、3つのステップで構成されています。

ステップ1

BaseActivity(またはすべてのActivitys)のonCreate()で、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を設定します。

ただし、言語の方向を正しく設定するには、もう1つの手順が必要です...

ステップ

BaseActivityonCreate()に、次のコードを追加します。

_@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()関数は次のように定義されています。

_public static boolean isRTL(){
    return TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;
}
_

上記の手順は、Androidの現存するすべてのバージョンでのすべての問題(少なくともLocaleとテキストの方向の設定に関して)に対処する必要があります。

3
Y.S

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

注意

github repoからソースコードをダウンロードできます

4
Nilesh Rathod

メソッド Resources#updateConfiguration (Configuration config, DisplayMetrics metrics)APIレベル25で廃止されました

ドキュメントは 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(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()は言語コードを返す必要があります。通常、言語コードまたはそれを表すその他のデータは、設定に保存されます。
  • 言語を動的に変更するには、適切な言語コードをプリファレンスに設定した後でアクティビティを再作成します。
  • アプリケーションコンテキストではなくアクティビティコンテキストを使用して、ロケール固有のリソースにアクセスします。つまり、getActivity()ではなく、アクティビティからthisまたは_ActivityName.this_を使用し、フラグメントからgetApplicationContext()を使用します。
3

アプリがバックグラウンドでキャッシュを作成しているため、アプリを完全に閉じました。

以下のコードを使用して、私の場合にどのようにそれを達成したかを確認してください。

 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);
0
Diwakar Singh
_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");

その後、アプリケーションを再起動して言語変更の効果を得る必要があります。

0
SHIDHIN.T.S

Resources.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")));
}
0
svkaka