为了账号安全,请及时绑定邮箱和手机立即绑定

Android-WebView语言在Android 7.0及更高版本上突然改变

/ 猿问

Android-WebView语言在Android 7.0及更高版本上突然改变

呼唤远方 2019-12-27 11:02:31

我有使用主要语言英语和次要语言阿拉伯语的多语言应用程序。


如文档中所述,


我已经添加android:supportsRtl="true"了清单。

我已将所有xml属性分别与left和right属性分别更改为start和end。

我在中添加了阿拉伯语言字符串strings-ar(其他资源也是如此)。

上面的设置可以正常工作。将更Locale改为后ar-AE,阿拉伯文和资源会正确显示在我的活动中。


但是,每次我Activity使用WebView 和和/或导航到时WebViewClient,语言环境,文本和布局方向都会突然恢复为设备默认值。


更多提示:


这仅在具有Android 7.0的Nexus 6P上发生。一切在Android 6.0.1及更低版本上均可正常运行。

仅当我导航到Activity具有WebView和(或)WebViewClient(并且我有多个)的时,才会发生语言环境的突然变化。在任何其他活动上都不会发生。

Android 7.0支持多语言环境,允许用户设置多个默认语言环境。因此,如果我将主要语言环境设置为Locale.UK:


在此处输入图片说明


然后在导航到时WebView,语言环境从更改ar-AE 为en-GB。


Android 7.0 API更改:


如API更改列表中所示,与语言环境有关的新方法已添加到API 24中的以下类:


Locale:


Locale.getDefault(...)

Locale.setDefault(...)

Configuration:


getLocales()

setLocales(...)

但是,我正在使用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());

}

有人遇到过这个问题吗?原因是什么,我该如何解决?


查看完整描述

3 回答

?
慕慕森

背景:


Android WebView是使用WebKit构建的。虽然它最初是AOSP的一部分,但从KitKat开始,便决定将其WebView拆分为一个名为Android System WebView的单独组件。本质上,它是一个预装有Android设备的Android系统应用。就像其他系统应用(例如Google Play服务和Play商店应用)一样,它会定期更新。您可以在已安装的系统应用程序列表中看到它:


Android系统WebView


Android 7.0的变化:


从Android N开始,Chrome应用将用于渲染WebView第三方Android应用中的任何/全部。在开箱即用的Android N手机中,根本没有Android WebView System应用程序。在已收到Android N的OTA更新的设备中,Android系统WebView被禁用:


WebView已禁用



WebView已禁用


此外,已引入多语言环境支持,并且设备具有多种默认语言:


在此处输入图片说明


对于具有多种语言的应用程序,这具有重要的意义。如果您的应用包含WebView,则这些内容将使用Chrome应用呈现。由于Chrome 本身就是一个Android应用,因此以其自己的沙盒流程运行,因此不会绑定到您的应用设置的语言环境。取而代之的是,Chrome将还原为主要设备的语言环境。例如,假设您的应用语言环境设置为ar-AE,而设备的主要语言环境设置为en-US。在这种情况下,Activity包含的语言环境WebView将从ar-AE变为en-US,并且将显示来自相应语言环境文件夹的字符串和资源。您可能会在Activity具有WebViews的那些s 上看到大量的LTR和RTL字符串/资源。


解决方案:


完整的解决方案包括两个步骤:


步骤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您希望设置的默认值。例如,如果您希望将阿拉伯语/阿拉伯联合酋长国设置为默认语言环境,则应传递new Locale("ar", "AE")。或者,如果您希望设置默认语言环境(即Locale由操作系统自动设置的语言环境),则应通过Locale.US。


第2步:


此外,您需要添加以下代码行:


new WebView(this).destroy();

在onCreate()您的Application类(如果有的话),和任何其他地方的用户可以更改语言。这将处理在更改语言后重新启动应用程序时可能发生的各种极端情况(您可能已经注意到其他语言的字符串,或者在更改语言后在Android 7.0 ++ 上Activities具有相反的对齐方式WebView)。


作为附录,Chrome自定义标签现在是呈现应用内网页的首选方式。


查看完整回答
反对 回复 2019-12-27
?
炎炎设计

您的代码似乎正在为应用本身(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()方法。setContentView()


查看完整回答
反对 回复 2019-12-27
?
米琪卡哇伊

阅读完所有答案后,我发现每个答案中都缺少一些东西,因此这是迄今为止对我有用的解决方案。由于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后(例如,按回去和onStop时)将调用fix方法。如果对话框中显示了webView,则必须注意在关闭对话框后调用fix方法,主要是在onResume和/或onCreate中。并且,如果将webView直接加载到Activity的onCreate中,而不是随后加载到新片段中,则还必须在setContentView之后直接调用该修订,然后再设置活动的标题等。如果将WebView加载到Activity中的片段中,则调用片段的onViewCreated中的活动,该活动应调用fix方法。并非所有的活动都需要按照习题所述扩展以上的类,这是一个过大的技巧,而且不是必需的。通过Google Chrome Tabs替换WebView或打开外部浏览器也无法解决此问题。


如果你真的需要你的ressources configuratoin有集语言的整个列表,而不是只有一个,那么你就需要在合并与一个该解决方案https://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce 在我的情况下,它是不必要。


我也没有发现调用新的WebView(this).destroy()的必要。如此处的答案所述。


查看完整回答
反对 回复 2019-12-27

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信