一、前言
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()就好了。这样是不会有什么问题的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍Android 获取系统语言的实例(兼容7.0),包括了Android 获取系统语言的实例(兼容7.0)的使用技巧和注意事项,需要的朋友参考一下 前言 获取系统当前语言是一个比较常用的功能,在 Android 7.0 系统上旧函数获取到的当前系统语言并不正确,或者说从 Android 7.0 起,Android 系统语言的规则变了。 下面是未适配 Android 7.0 的代码: 由
本文向大家介绍C语言 位域详解及示例代码,包括了C语言 位域详解及示例代码的使用技巧和注意事项,需要的朋友参考一下 有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用 0 和 1 表示足以,也就是用一个二进位。正是基于这种考虑,C语言又提供了一种叫做位域的数据结构。 在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),
本文向大家介绍Java语言实现反转链表代码示例,包括了Java语言实现反转链表代码示例的使用技巧和注意事项,需要的朋友参考一下 问题描述 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后的链表的头结点。链表结点如下: 思路1: 要想反转链表,对于结点i,我们要把它的next指向它的前趋,因此我们需要保存前趋结点,同时,如果我们已经把i的next重新赋值,会无法找到i的后继,因此,在重新赋
本文向大家介绍C语言 位运算详解及示例代码,包括了C语言 位运算详解及示例代码的使用技巧和注意事项,需要的朋友参考一下 所谓位运算,就是对一个比特(Bit)位进行操作。在《二进制思想以及数据的存储》一节中讲到,比特(Bit)是一个电子元器件,8个比特构成一个字节(Byte),它已经是粒度最小的可操作单元了。 C语言提供了六种位运算符: 运算符 & | ^ ~ << >> 说明 按位与 按位或 按位
本文向大家介绍go语言实现聊天服务器的示例代码,包括了go语言实现聊天服务器的示例代码的使用技巧和注意事项,需要的朋友参考一下 看了两天 go 语言,是时候练练手了。 go 的 routine(例程) 和 chan(通道) 简直是神器,实现多线程(在 go 里准确的来说是 多例程)简直不要太轻松。 于是动手码了一个傻瓜版的黑框聊天器。 server 端: 监听 TCP 连接;支持自定义客户端命令;
本文向大家介绍ionic2屏幕适配实现适配手机、平板等设备的示例代码,包括了ionic2屏幕适配实现适配手机、平板等设备的示例代码的使用技巧和注意事项,需要的朋友参考一下 本文介绍了ionic2屏幕适配实现适配手机、平板等设备的示例代码,分享给大家,具体如下: 推荐使用的编辑器是:VS code (Visual Studio Code)=>只负责编辑文档,不编译。 而WebStorm 有检查编译