在每个车载系统中,都会有自己独特的UI风格。比如系统设置这个功能,为了统一风格,我们不能直接去调用Android原生的设置应用,而是应该重新做一个风格统一的符合需求的系统设置应用。我们要说的语言切换这个功能,就是其中一个设置项。
如果简单地实现语言切换,应该是比较简单的,就是利用反射重新设置一下Locale就好了,这个时候Activity会异常销毁,并会重新加载刷新当前语言资源。如下:
public static void setLanguage(Locale locale){
try {
Class c = Class.forName("com.android.internal.app.LocalePicker");
Object obj = c.newInstance();
Method method = c.getMethod("updateLocale", new Class[] {Locale.class});
method.invoke(obj, locale);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}catch (NoSuchMethodException e) {
e.printStackTrace();
}catch (IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
但是客户的需求可没这么简单,首先他不会允许你有一个销毁再新创建的闪一下的动作,其次会要求增加一个带loading动画的弹窗效果。
第一个需求的解决,是在Manifest文件中对应的设置语言的那个Activity增加属性: android:configChanges=“locale|layoutDirection”。这样Activity就不会自动销毁了。但同时,我们需要注册一个广播接收器,监听语言改变的广播,在这里需要把Activity里面的相关字符串重新加载一遍,实现手动的刷新。如下:
/** 注册语言改变广播 */
private void registerLocalReceiver() {
IntentFilter mFilter = new IntentFilter();
mFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
this.registerReceiver(localReceiver, mFilter);
}
private BroadcastReceiver localReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context arg0, Intent arg1) {
Log.i(TAG, "receive the language changed broadcast!");
initData();
}
};
@Override
public void initData() {
mTvTitle.setText(getStr(R.string.set_usually));
mIvTitle.setImageResource(R.drawable.set_usually_n);
.......
}
第二个需求,需要在切换语言的同时,还要有一个loading动画的弹窗效果。如果只是单纯地在应用中使用windowManager添加一个view,并利用动画去转动,很大概率会出现动画卡顿的效果,主要是动画的操作,只能放在主线程去做。于是可以把语言切换和动画的效果,放在另外的进程去处理,这样就可以实现平滑的语言切换了。弹窗的实现效果如下:
private void showUpdateLanguageDialog(View view){
try{
WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams mWindowParams = new WindowManager.LayoutParams();
mWindowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
mWindowParams.x = 0;
mWindowParams.y = 0;
mWindowParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mWindowParams.width = WindowManager.LayoutParams.MATCH_PARENT;
mWindowParams.height = WindowManager.LayoutParams.MATCH_PARENT;
mWindowManager.addView(view, mWindowParams);
}catch(Exception ex){
}
}
由于Locale是不能通过Parcelable传递的,所以使用Configuration来传递,切换语言也可以用下面的方式:
IActivityManager iActMag = ActivityManagerNative.getDefault();
try {
Configuration config = iActMag.getConfiguration();
config.locale = locale;
// 此处需要声明权限:android.permission.CHANGE_CONFIGURATION
// 会重新调用 onCreate();
iActMag.updateConfiguration(config);
} catch (RemoteException e) {
e.printStackTrace();
}