专栏首页恩蓝脚本Android多语言适配的示例代码(兼容7.0+)

Android多语言适配的示例代码(兼容7.0+)

一、前言

1、安卓系统本身对多语言适配就提供了一套框架和API。我们就直接用就可以了。

2、更换语言必须recreate Activity。目前,没见过可以不重建的方法。常用App,也都是重建的,可以看的到。

3、兼容性问题。现在越来越多设备都是安卓7.0+新手机的安卓版本会更高(安卓8.0+),所以适配是必要的。

4、目前,网上大部分相关文章都是不兼容7.0+的,具体做法一搜一大把。

二、具体做法

1、多语言文件

文件夹命名参考下面博客(网上有很多):

多国语言value文件夹命名

value默认放英文的资源文件,简体中文文件夹命名为values-zh-rCN,不需要翻译的设置translatable如下:

代码如下:

 <string name=”app_name_english” translatable=”false” You App English Name</string 

2、多语言工具类

public class LanguageUtils {
public static final String CHINESE_SIMPLE = "zh_CN";
public static final String ENGLISH = "en";
public static final String AUTO = "auto";
private static final String TAG = "LanguageUtils";
//public static final String[] LOCALES = Utils.getContext().getResources().getStringArray(R.array.locales);
private LanguageUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
public static void setSystemDefaultLocale(Locale locale) {
}
public static boolean isSetValue(Context context) {
Locale currentLocale = context.getResources().getConfiguration().locale;
return currentLocale.equals(getSetLocale());
}
private static Locale getSetLocale() {
String locale = SPUtils.getInstance(BaseConstants.SP.NAME_APP_SETTINGS).getString(BaseConstants.SP.KEY_LANGUAGE, LanguageUtils.AUTO);
if (locale.equals(LanguageUtils.AUTO)) {
if (Build.VERSION.SDK_INT  = Build.VERSION_CODES.N) {
return Resources.getSystem().getConfiguration().getLocales().get(0);//解决了获取系统默认错误的问题
} else {
return Locale.getDefault();
}
}
String[] array = locale.split("_");
String language = array[0];
if (array.length   1) {
String country = array[1];
return new Locale(language, country);
}
return new Locale(language);
}
public static int getSetIndex() {
String languageSet = SPUtils.getInstance(BaseConstants.SP.NAME_APP_SETTINGS).getString(BaseConstants.SP.KEY_LANGUAGE, LanguageUtils.AUTO);
int localeIndex = 0;
switch (languageSet) {
case LanguageUtils.AUTO:
localeIndex = 0;
break;
case LanguageUtils.CHINESE_SIMPLE:
localeIndex = 1;
break;
case LanguageUtils.ENGLISH:
localeIndex = 2;
break;
}
return localeIndex;
}
public static Context wrapContext(Context context) {
Resources resources = context.getResources();
Locale locale = LanguageUtils.getSetLocale();
Configuration configuration = resources.getConfiguration();
if (Build.VERSION.SDK_INT  = Build.VERSION_CODES.N) {
configuration.setLocale(locale);
LocaleList localeList = new LocaleList(locale);
LocaleList.setDefault(localeList);
configuration.setLocales(localeList);
} else {
configuration.setLocale(locale);
}
return context.createConfigurationContext(configuration);
}
public static void applyChange(Context context) {
Resources res = context.getResources();
DisplayMetrics dm = res.getDisplayMetrics();
Configuration conf = res.getConfiguration();
Locale locale = getSetLocale();
if (Build.VERSION.SDK_INT  = Build.VERSION_CODES.N) {
conf.setLocale(locale);
LocaleList localeList = new LocaleList(locale);
LocaleList.setDefault(localeList);
conf.setLocales(localeList);
} else {
conf.setLocale(locale);
}
res.updateConfiguration(conf, dm);
}
}

3、代码分析&兼容7.0+

3.1、如何获取系统的语言设置,也就是7.0+你选择auto,可以正确切换。

if (Build.VERSION.SDK_INT  = Build.VERSION_CODES.N) {
return Resources.getSystem().getConfiguration().getLocales().get(0);//解决了获取系统默认错误的问题
} else {
return Locale.getDefault();
}

看到这篇文章的你,可能已经看过网上很多其他相关的文章,应该知道,7.0+系统有个很奇怪的地方:

如果你在app内切换了语言(比如说是英文),且该语言和系统的设置(比如说是中文)不同,那么你再次切换语言并选择auto时,通过Locale.getDefault()获取会错误,或者你通过LocaleList.get(0)也是错误的,你之前选择的语言(英文)排序被提前了。有些文章的解决方案是在app打开时持久化系统设置,这样你切换app的语言就不会影响你获取系统的设置,但这样没必要,太麻烦(应该是不知道上面的方法)。

7.0+的系统设置也看的出差别,以前,设置系统语言直接选择就可以了,现在你要先添加,然后再排序,排在第一个的才是系统显示的语言!

3.2、写个BaseActivity作为所有Activity父类

新建一个BaseActivity用于继承,重写:

@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(LanguageUtils.wrapContext(newBase));
}

然后在切换语言后,你要recreate Activity。这个在哪调用就看具体需求了。你可以像微信那样,清空栈,然后直接重启到主界面,也可以在设置界面recreate,但栈内其他Activity,也要想办法通知recreate。

3.3、屏蔽系统设置改变

如果app的语言选项不是auto,那么系统语言设置修改时,app就不应该跟着系统变,而是按照自己设置的语言显示。写一个类继承于Application(注意要在manifest配置哦,不然无效的)

public class MyApp extends Application {
private Configuration deltaConfig;
@Override
public void onConfigurationChanged(Configuration newConfig) {
LogUtils.d(TAG, "调用了onConfigurationChanged");
int diff = newConfig.diff(deltaConfig);
String languageSet = SPUtils.getInstance(AppConstants.SP.NAME_APP_SETTINGS).getString(AppConstants.SP.KEY_LANGUAGE, LanguageUtils.AUTO);
if (languageSet.equals(LanguageUtils.AUTO)) {//看app语言设置是不是auto,是的话不管,直接super
super.onConfigurationChanged(newConfig);
deltaConfig = newConfig;
} else if (diff != ActivityInfo.CONFIG_LOCALE) {//这个Configuration更改是不是语言,不是的话,也不管
super.onConfigurationChanged(newConfig);
deltaConfig = newConfig;
}
//这里使系统设置语言无效
//相当于省略了
//else{
// return;
//}
}
@Override
public void onCreate() {
super.onCreate();
//app打开时记录系统设置
deltaConfig = getApplicationContext().getResources().getConfiguration();
LanguageUtils.applyChange(getApplicationContext());
}
}
}

3.4、其他问题

Application的Context也要更新

LanguageUtils.applyChange(context);
LanguageUtils.applyChange(context.getApplicationContext());

但即使这样,还是有点问题,主要在于:

如果Activity的Title你是在manifest中定义的,如下label:

<activity
android:name=".ui.activity.AboutActivity"
android:launchMode="singleTop"
android:label="@string/lable_activity_about"
android:theme="@style/AppTheme.NoActionBar"/ 

那么,即使你更新了ApplicationContext,有些Activity也有可能不生效,而且每次都还不一样,这个没法复现(很迷)。不知道是不是系统bug(测试系统是一加3 氢OS 8.0),或者是有其他更好的写法?

针对这个问题,只要在activity oncreate() 里setTitle()就好了。这样是不会有什么问题的。

以上就是本文的全部内容,希望对大家的学习有所帮助。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android使用AsyncQueryHandler实现获取手机联系人功能

    利用AsyncQueryHandler能异步任务获取手机联系人,增加用户体验,使用起来也很方便。不多说,上干货。

    砸漏
  • Android使用注解代替枚举节省系统内存开销的方法

    Java5以后开始支持枚举类型,枚举类型使用起来非常方便,其重要的作用是作为类型安全使用的。如果在不考虑系统内存开销的情况下大量的使用枚举也不会有什么问题。但是...

    砸漏
  • Android WebView交互传递json字符串并解析的方法

    我们大家都知道WebView交互中可以传递基本数据类型的数据值,比如常用的int,String.

    砸漏
  • Android点将台:你敢摸我猫 [- IPC -]

    张风捷特烈
  • 18.手写Spring MVC

    Serviet 的生命周期由 init()到 service()再到 destory()组成, destory()方法我们 不做实现。

    编程之心
  • 八、适配器模式与桥接模式详解

    适配器模式的英文翻译是 Adapter Design Pattern。顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼...

    编程之心
  • ”MySQL官方驱动“主从分离的神秘面纱(扫盲篇)

    假如你日后的工作,需要快速实现MySQL的读写分离功能,你一定会想起这篇文章。如果你再次回到这里,证明你已经迫切需要一个简单快捷的解决方案了—那就是MySQL官...

    xjjdog
  • 16-Flink-Redis-Sink

    流式计算中,我们经常有一些场景是消费Kafka数据,进行处理,然后存储到其他的数据库或者缓存或者重新发送回其他的消息队列中。

    大数据技术与架构
  • SpringBoot2.0 整合 SpringSecurity 框架,实现用户权限管理

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配...

    知了一笑
  • 16-Flink-Redis-Sink

    流式计算中,我们经常有一些场景是消费Kafka数据,进行处理,然后存储到其他的数据库或者缓存或者重新发送回其他的消息队列中。

    大数据技术与架构

扫码关注云+社区

领取腾讯云代金券