web-dev-qa-db-ja.com

Android-Android 7.0以降でWebView言語が突然変更される

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

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

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

上記のセットアップは正常に機能します。 Localeを_ar-AE_に変更すると、アクティビティにアラビア語のテキストとリソースが正しく表示されます。

ただし、Activityおよび/またはWebViewを使用してWebViewClientに移動するたびに、ロケール、テキスト、およびレイアウトの方向が突然デバイスのデフォルトに戻ります。

その他のヒント:

  • これは、のみがNexus 6PでAndroid 7.0。すべて正常に動作しますAndroid 6.0.1以下。
  • ロケールの突然のシフトは、Activityおよび/またはWebViewを含むWebViewClientにナビゲートするときに(およびいくつかあります)、onlyで発生します。他のアクティビティでは発生しません。

Android 7.0はマルチロケールをサポートしており、ユーザーは複数のデフォルトロケールを設定できます。したがって、プライマリロケールを_Locale.UK_に設定すると:

enter image description here

次に、WebViewに移動すると、ロケールが_ar-AE_から_en-GB_に変更されます。

Android 7.0 APIの変更:

API変更のリスト に示されているように、ロケールに関する新しいメソッドがAPI 24の次のクラスに追加されました。

Locale

Configuration

ただし、API 23でアプリを構築していますが、これらの新しいメソッドは使用していません。

さらに...

  • この問題は、Nexus 6Pエミュレーターでも発生します。

  • デフォルトのロケールを取得するには、 Locale.getDefault() を使用しています。

  • デフォルトのロケールを設定するには、次のコードを使用しています。

    _public static void setLocale(Locale locale){
        Locale.setDefault(locale);
        Configuration config = new Configuration();
        config.setLocale(locale);
        Context context = MyApplication.getInstance();
        context.getResources().updateConfiguration(config,
                context.getResources().getDisplayMetrics());
    }
    _

誰もこの問題に遭遇したことがありますか?その理由は何ですか?これを解決するにはどうすればよいですか?

参照:

1.Android 4.2 。でのネイティブRTLサポート。

2.多言語サポート-言語とロケール

3.デフォルトのロケールに注意してください

54
Y.S

テッド・ホップの答え 問題を解決できたが、彼はの問題に対処しなかったwhyこれが発生する。

理由は、 WebView クラスとAndroid 7.0のサポートパッケージに加えられた変更です。

背景:

AndroidのWebViewは、 WebKit を使用して構築されます。当初はAOSPの一部でしたが、KitKat以降、WebViewAndroid System WebView と呼ばれる別のコンポーネントに分割する決定が下されました。 AndroidデバイスがプレインストールされているAndroidシステムアプリです。 Google Play ServicesやPlayストアアプリなどの他のシステムアプリと同様に、定期的に更新されます。インストールされているシステムアプリのリストで確認できます。

Android System WebView

Android 7.0の変更点

Android Nで始まるChromeアプリは、サードパーティAndroidアプリのWebViewsをレンダリングするために使用されます。 Android Nがすぐに使える電話では、Android WebView Systemアプリはまったく存在しません。 Android NへのOTA更新を受信したデバイスでは、Android System WebViewが無効になります。

WebView disabled

そして

WebView disabled

さらに、複数ロケールのサポートが導入され、デバイスには複数のデフォルト言語があります。

enter image description here

これは、複数の言語を持つアプリにとって重要な結果をもたらします。アプリにWebViewsがある場合、Chromeアプリを使用してレンダリングされます。 ChromeはAndroidアプリin自体であり、独自のサンドボックスプロセスで実行されるため、アプリによって設定されたロケールにバインドされません。代わりに、Chromeはプライマリデバイスのロケールに戻ります。たとえば、アプリのロケールが_ar-AE_に設定されており、デバイスのプライマリロケールが_en-US_であるとします。この場合、Activityを含むWebViewのロケールは_ar-AE_から_en-US_に変更され、対応するロケールフォルダーの文字列とリソースが表示されます。 Activitysを持つWebViewsには、LTRおよびRTLの文字列/リソースのミッシュマッシュが表示される場合があります。

ソリューション:

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

ステップ1:

最初に、すべてのActivity、または少なくともActivityを持つすべてのWebViewでデフォルトのロケールを手動でリセットします。

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

    if (Build.VERSION.SDK_INT >= 25) {
        context = context.getApplicationContext().createConfigurationContext(configuration);
        context = context.createConfigurationContext(configuration);
    }

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

すべてのアクティビティのsetContentView(...)メソッドでonCreate()を呼び出す前に、上記のメソッドを呼び出します。 localeパラメーターは、設定するデフォルトのLocaleである必要があります。たとえば、アラビア語/ UAEをデフォルトロケールとして設定する場合は、new Locale("ar", "AE")を渡す必要があります。または、デフォルトのロケール(つまり、オペレーティングシステムによって自動的に設定されるLocale)を設定する場合は、_Locale.US_を渡す必要があります。

ステップ2:

さらに、次のコード行を追加する必要があります。

_new WebView(this).destroy();
_

ApplicationクラスのonCreate()(ある場合)、およびユーザーが言語を変更する可能性のある場所これにより、言語を変更した後のアプリの再起動で発生する可能性のあるすべての種類のEdgeケースが処理されます(ActivitiesWebViews)。

補遺として、 Chromeカスタムタブ がアプリ内Webページのレンダリングの推奨方法になりました。

参照:

1。Android 7.0-WebView の変更。

2。WebViewとAndroidセキュリティパッチを理解する

3。WebView for Android .

4。WebView:「Powered by Chrome」からChrome へ。

5。Nougat WebView

6。Android 7.0 Nougat

7。Android N Mysteries、Part 1:Android System WebView is just "Chrome" Now?

57
Y.S

コードは、アプリ自体の構成でロケールを設定しているようです(MyApplication.getInstance())。ただし、アクティビティのコンテンツビューを拡張する前に、アクティビティコンテキストの構成を更新する必要があります。アプリのコンテキストを変更するだけでは不十分であることがわかりました(そして、結局のところ、それは不要です)。各アクティビティコンテキストを更新しない場合、動作はアクティビティ間で一貫性がありません。

これにアプローチする方法は、AppCompatActivity(または互換性ライブラリを使用しない場合はActivity)をサブクラス化し、そのサブクラスからすべてのアクティビティクラスを派生させることです。ここに私のコードの簡略版があります:

_public class LocaleSensitiveActivity extends AppCompatActivity {
    @Override protected void onCreate(Bundle savedInstanceState) {
        Locale locale = ... // the locale to use for this activity
        fixupLocale(this, locale);
        super.onCreate(savedInstanceState);
        ...
    }

    static void fixupLocale(Context ctx, Locale newLocale) {
        final Resources res = ctx.getResources();
        final Configuration config = res.getConfiguration();
        final Locale curLocale = getLocale(config);
        if (!curLocale.equals(newLocale)) {
            Locale.setDefault(newLocale);
            final Configuration conf = new Configuration(config);
            conf.setLocale(newLocale);
            res.updateConfiguration(conf, res.getDisplayMetrics());
        }
    }

    private static Locale getLocale(Configuration config) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return config.getLocales().get(0);
        } else {
            //noinspection deprecation
            return config.locale;
        }
    }
}
_

次に、各サブクラスのsuper.onCreate(savedInstanceState)メソッドでonCreate()を必ず呼び出しますbeforeコンテキストを使用するメソッド(setContentView()など)を呼び出します。

20
Ted Hopp

すべての答えを読んだ後、それぞれに欠けているものがあることがわかったので、ここでこれまでのところうまくいった解決策を示します。 WebViewはアクティビティのコンテキストとアプリケーションコンテキストの言語構成をオーバーライドするため、これが発生するたびに、それらの変更をリセットするメソッドを呼び出すことを確認する必要があります。私の場合、この問題を提示する私の活動(WebViewを表示する活動)を拡張する次のクラスを書きました。

public class WebViewFixAppCompatActivity extends AppCompatActivity {

private Locale mBackedUpLocale = null;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        mBackedUpLocale = getApplicationContext().getResources().getConfiguration().getLocales().get(0);
    }
}

@Override
protected void onStop() {
    super.onStop();
    fixLocale();
}

@Override
public void onBackPressed() {
    fixLocale();
    super.onBackPressed();
}

/**
 * The locale configuration of the activity context and the global application context gets overridden with the first language the app supports.
 */
public void fixLocale() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        Resources resources = getResources();
        final Configuration config = resources.getConfiguration();

        if (null != mBackedUpLocale && !config.getLocales().get(0).equals(mBackedUpLocale)) {
            Locale.setDefault(mBackedUpLocale);
            final Configuration newConfig = new Configuration(config);
            newConfig.setLocale(new Locale(mBackedUpLocale.getLanguage(), mBackedUpLocale.getCountry()));
            resources.updateConfiguration(newConfig, null);
        }

        // Also this must be overridden, otherwise for example when opening a dialog the title could have one language and the content other, because
        // different contexts are used to get the resources.
        Resources appResources = getApplicationContext().getResources();
        final Configuration appConfig = appResources.getConfiguration();
        if (null != mBackedUpLocale && !appConfig.getLocales().get(0).equals(mBackedUpLocale)) {
            Locale.setDefault(mBackedUpLocale);
            final Configuration newConfig = new Configuration(appConfig);
            newConfig.setLocale(new Locale(mBackedUpLocale.getLanguage(), mBackedUpLocale.getCountry()));
            appResources.updateConfiguration(newConfig, null);
        }

    }
}
}

@Tobliugによって投稿されたアイデアは、WebViewがオーバーライドする前に初期設定を保存するために機能しましたが、私の特定のケースでは、これは他のソリューションよりも簡単に実装できることがわかりました。重要なのは、WebViewの終了後にfixメソッドが呼び出されることです。押して、onStopを押すとき。 webViewがダイアログに表示される場合、ダイアログを閉じた後に、ほとんどの場合onResumeやonCreateでfixメソッドが呼び出されるように注意する必要があります。また、webViewがアクティビティのonCreateに直接読み込まれ、その後新しいフラグメントに読み込まれない場合、アクティビティのタイトルが設定される前にsetContentViewの直後に修正を呼び出す必要があります。フラグメントのonViewCreatedのアクティビティとアクティビティは、fixメソッドを呼び出す必要があります。すべてのアクティビティが上記のクラスを拡張する必要があるわけではありませんが、それは過剰であり、必要ではありません。この問題は、WebViewをGoogle Chrome Tabsで置き換えるか、外部ブラウザーを開くことで解決することもできません。

Ressources configuratoinで1つだけでなく言語のリスト全体を設定する必要がある場合は、このソリューションを https://Gist.github.comのソリューションとマージする必要があります。/amake/0ac7724681ac1c178c6f95a5b09f03ce 私の場合は必要ありませんでした。

また、新しいWebView(this).destroy();を呼び出す必要もありませんでした。こちらの回答に記載されています。

4
David

ここで同じ問題。私は汚いが、単純な解決策を持っています。

ロケールはActivity.onCreate(...)関数ではまだ有効で、Activity.onPostCreate(...)関数ではこれ以上有効ではないことに気付いたので、Localeを保存して、onPostCreateの最後に強制します。 (...) 関数。

さあ :

private Locale backedUpLocale = null;

@Override
protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    backedUpLocale = getApplicationContext().getResources().getConfiguration().locale;
}

@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    changeLocale(backedUpLocale);
}

ボーナス-ロケール変更機能:

public void changeLocale(final Locale locale) {

    final Configuration config = res.getConfiguration();

    if(null != locale && !config.locale.equals(locale)) {
        Locale.setDefault(locale);

        final Configuration newConfig = new Configuration(config);

        if(PlatformVersion.isAtLeastJellyBeanMR1()) {
            newConfig.setLocale(new Locale(locale.getLanguage()));
        } else {
            newConfig.locale = new Locale(locale.getLanguage());
        }

        res.updateConfiguration(newConfig, null);
    }
}

それが役立つことを願っています。

1
Tobliug

上記の回答のいずれも私を助けませんでした、私は内部でアプリのロケールをリセットすることに成功しましたonStop() Webviewを含むアクティビティのメソッド

0
MohammadReza

WebViewをリッチテキスト(いくつかの段落を含むテキスト、または異なるフォントサイズの太字と斜体のテキスト)のみを表示する場合は、代わりにTextViewと Html.fromHtml() を使用できます。 TextViewsにはロケール設定に関する問題はありません;-)

0
Ben

ここにもう1つuse-caseを追加します。

Webviewアクティビティから戻る(つまり、支払い画面を表示し、ユーザーが戻るボタンを押す)と、前のアクティビティのonCreate()が実行されないため、その言語が再びリセットされました。バグをなくすために、ベースアクティビティのonResume()でアプリのロケールをリセットする必要があります。

private static void updateResources(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);
    Configuration config = new Configuration();
    config.setLocale(locale);
    config.setLayoutDirection(locale);
    context.getResources().updateConfiguration(config,
            context.getResources().getDisplayMetrics());
}

上記のメソッドをベースアクティビティのonResume()で呼び出すか、少なくともwebviewアクティビティで呼び出します。

編集:フラグメントを処理している場合、ユーザーがwebviewを終了するときにこのメソッドが呼び出されることを確認してください。

0