最近做了个小项目,领导要求添加可以切换中英文的功能,自己实现了这么一个效果:
这里说一下我的实现思路:
1.编写不同语言环境的string.xml文件
2.编写一个工具类修改程序的配置信息
3.销毁重建activity使配置生效
4.重建activity后根据在主线程中存储的语言标识修改按钮图标(右上角的按钮)
1.编写不同语言环境的string.xml文件
android studio生成不同语言环境的string.xml文件
参考:
Android studio 添加多语言支持
2.编写一个工具类修改程序的配置信息
package com.phyplusinc.android.phymeshprovisioner.utils;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.Locale;
/**
* @Author: david.lvfujiang
* @Date: 2019/12/11
* @Describe:语言切换工具类
*/
public class LanguageUtil {
//弱引用持有Context
private static WeakReference<Context> weakReference;
public static String settingLanguage(Context context, ThreadLocal threadLocal) {
weakReference = new WeakReference<>(context);
//获得res资源对象
Resources resources = weakReference.get().getResources();
//获得设置对象
Configuration config = resources.getConfiguration();
//获得屏幕参数:主要是分辨率,像素等。
DisplayMetrics dm = resources.getDisplayMetrics();
//使用ThreadLocal获取主线程的数据
String language = (String) threadLocal.get();
if ("English".equals(language)) {
// 设置语言为中文
config.locale = Locale.CHINESE;
threadLocal.set("Chinese");
Log.e("TAG", "Chinese");
} else {
//英语(美国)
config.locale = Locale.US;
threadLocal.set("English");
Log.e("TAG", "English");
}
//修改配置
resources.updateConfiguration(config, dm);
return language;
}
//获取ThreadLocal对象
public static ThreadLocal getInstance() {
return Holder.threadLocal;
}
//在静态内部类实现的单例模式
private static class Holder {
private static ThreadLocal threadLocal = new ThreadLocal<String>();
}
}
这个工具类主要的作用就是修改app的语言配置,这个过程需要
context
上下文,为了防止内存泄漏我使用了弱引用持有context
。并且使用ThreadLocal
单例对象(静态内部类实现单例模式)在主线程中存一个语言标识language
,ThreadLocal
的源码与及应用场景可以参考我的这篇博客:android开发:ThreadLocal使用场景与源码解析
然后在
activity
中需要切换语言时就调用工具类修改配置,然后销毁重建activity配置才会生效
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.img_language:
//修改配置
LanguageUtil.settingLanguage(MainActivity.this,LanguageUtil.getInstance());
//activity銷毀重建
MainActivity.this.recreate();
break;
default:
break;
}
activity銷毀重建会创建新的activity对象,系统会通过
onSaveInstanceState()
保存一些数据,activity重建时会回调onRestoreInstanceState()
方法。因此我重写onRestoreInstanceState()
在其中获取通过ThreadLocal
获取主线程的语言标识来选择切换不同的图片。
@Override
public void onRestoreInstanceState(Bundle bundle){
super.onRestoreInstanceState(bundle);
Log.e("TAG","onRestoreInstanceState");
activityReset();
}
/**
* activity銷毀重建后会执行这个方法
*/
public void activityReset() {
//同一个对象在同一个线程中获取的数据是一样的,因为每个线程都维护了一个ThreadLocalMap对象,key值是ThreadLocal
ThreadLocal<String> threadLocal = LanguageUtil.getInstance();
String language = threadLocal.get();
if ("English".equals(language)) {
imgLanguage.setImageResource(R.mipmap.chinese);
} else if ("Chinese".equals(language)) {
imgLanguage.setImageResource(R.mipmap.english);
}
}
这个例子功能其实很简单,但是我考虑到的东西还挺多,单例模式、内存泄露、ThreadLocal等。因此在写代码的时候不要止单单实现功能而已,应该学会思考这样写有什么问题吗?有没有更好的写法?