• [Android]应用语言切换的三种方法


        Android对国际化与多语言切换已经做得不错了,一个应用只要命名相应语系的values-[language]文件夹,通过“设置”→“语言&键盘”→“选择语言”即可实现应用多种语言的切换。    
        但如何在应用里自己实现?搜索过发现网上有如下的做法:
    1. Resources res = getResources();  
    2. Configuration config = res.getConfiguration();  
    3. config.locale = locale;  
    4. DisplayMetrics dm = res.getDisplayMetrics();  
    5. res.updateConfiguration(config, dm);  
      
        亲测,不成功。好吧,程序员又到了自力更生的时候了。下面开始讲应用多语言切换的三种方法。
        
        先上效果图:

    English  

    前两种方法的原理即在应用里实现“选择语言”。通过查看源码,其核心代码为:

    1. IActivityManager iActMag = ActivityManagerNative.getDefault();  
    2. try {  
    3.     Configuration config = iActMag.getConfiguration();  
    4.     config.locale = locale;  
    5.     // 此处需要声明权限:android.permission.CHANGE_CONFIGURATION  
    6.     // 会重新调用 onCreate();  
    7.     iActMag.updateConfiguration(config);  
    8. catch (RemoteException e) {  
    9.     e.printStackTrace();  
    10. }  
    11. PS:感谢 曾阳 的帮助。  
        可以发现IActivityManager与ActivityManagerNative都是非公开类。如何调用?第一种是API欺骗,第二种是使用Java反射机制。
        1. API欺骗
        烧制到手机中的android.jar包含了Android所需的各种类与方法;而供开发者使用的android.jar只是其中的一部分。API欺骗是指在应用中去模拟未公开的类和方法让应用编译通过并生成APK,然而在应用实际运行中调用的却仍是烧制到手机中真实的android.jar。
        
        通过核心代码可以看到我们要模拟的是ActivityManagerNative中的一个方法getDefault()和IActivityManager中的两个方法getConfiguration()与updateConfiguration(config)。参照源码,应用的工程结构图及代码模拟如下:
        
        工程结构图:

    代码:

    1. ActivityManagerNative.java  
    2. package android.app;  
    3.   
    4. /** 
    5.  * @author Sodino E-mail:sodinoopen@hotmail.com 
    6.  * @version Time:2011-7-10 上午11:37:01 
    7.  */  
    8. public abstract class ActivityManagerNative {  
    9.     public static IActivityManager getDefault() {  
    10.         return null;  
    11.     }  
    12. }  
    13.   
    14. IActivityManager.java  
    15. package android.app;  
    16.   
    17. import android.content.res.Configuration;  
    18. import android.os.RemoteException;  
    19.   
    20. /** 
    21.  * @author Sodino E-mail:sodinoopen@hotmail.com 
    22.  * @version Time:2011-7-10 上午11:37:46 
    23.  */  
    24. public abstract interface IActivityManager {  
    25.     public abstract Configuration getConfiguration() throws RemoteException;  
    26.   
    27.     public abstract void updateConfiguration(Configuration paramConfiguration)  
    28.             throws RemoteException;  
    29. }  
        实现模拟了这两个类后,即可正常使用上面提到的转换语系的核心代码了。

        2. Java反射机制
        不多说了,Java反射机制入门教程:
        http://java.sun.com/developer/technicalArticles/ALT/Reflection/index.html
        之前写过的几个使用Java反射的例子:
        [Android]获取未安装的APK图标(原创非转帖)
        http://blog.csdn.net/sodino/article/details/6215224
        [Android]挂断、接听电话
        http://blog.csdn.net/sodino/article/details/6181610
        
        直接上代码:

    1. private void updateLanguage(Locale locale) {  
    2.     Log.d("ANDROID_LAB", locale.toString());  
    3.     try {  
    4.         Object objIActMag, objActMagNative;  
    5.         Class clzIActMag = Class.forName("android.app.IActivityManager");  
    6.         Class clzActMagNative = Class.forName("android.app.ActivityManagerNative");  
    7.         Method mtdActMagNative$getDefault = clzActMagNative.getDeclaredMethod("getDefault");  
    8.         // IActivityManager iActMag = ActivityManagerNative.getDefault();  
    9.         objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative);  
    10.         // Configuration config = iActMag.getConfiguration();  
    11.         Method mtdIActMag$getConfiguration = clzIActMag.getDeclaredMethod("getConfiguration");  
    12.         Configuration config = (Configuration) mtdIActMag$getConfiguration.invoke(objIActMag);  
    13.         config.locale = locale;  
    14.         // iActMag.updateConfiguration(config);  
    15.         // 此处需要声明权限:android.permission.CHANGE_CONFIGURATION  
    16.         // 会重新调用 onCreate();  
    17.         Class[] clzParams = { Configuration.class };  
    18.         Method mtdIActMag$updateConfiguration = clzIActMag.getDeclaredMethod(  
    19.                 "updateConfiguration", clzParams);  
    20.         mtdIActMag$updateConfiguration.invoke(objIActMag, config);  
    21.     } catch (Exception e) {  
    22.         e.printStackTrace();  
    23.     }  
    24. }  
        实际运行后,发现对当前系统设置了新的Locale后,不单自己的应用语系改变了,系统所有的应用语系都改变了。这肯定是不合理的。有一个解决办法是在应用界面退出前再次对系统设置成碑的Locale,不过个人不喜欢这样的办法,加之调用updateConfiguration()方法后,整个Activity会重新onCreate(),这个考虑Activity的生命周期可有点费劲了。于是有了下面这第三种方法。
        
        3. 自己转换语系(哈哈,这个名字很现实啊)
        动手实现嘛,啥都系统弄好了,那程序员的存在还有什么意义呢。
        自己转换语系有点麻烦,先看工程结构图:
        


        values/strings.xml与xml/english.xml的内容是相同的;values-zh-rCN/strings.xml与xml/chinese.xml的内容也是相同的。出现这样的冗余是因为生成APK时values下的内容都打到rasc去了,读取不了了。
        
        自己实现语系的转换需要考虑到:
        3.1  R.xxxxx.id与对应语系中文本串的对应(需要特别考虑到R.array.string字符串数组)。
        3.2 解析xml。
        3.3 设置语系后,所有界面元素的手动刷新。
        
        在xml中声明一个string是这个的格式:

    1. <string name="app_name">语言应用</string>  
        对应R文件会生成一个id指代该string
    1. public static final class string {  
    2.     public static final int app_name=0x7f050001;  
    3. }  

        3.1的问题就是如何实现id与string的匹配,解决方法为:
    1. Resources res = context.getResources();  
    2. String pkg = context.getPackageName();  
    3. String tag = "app_name";  
    4. int idTag = res.getIdentifier(tag, "string", pkg);  
        3.2 解析XML
        这儿要用到一个新的工具了:XmlResourceParser,解析过程有点绕,但比SAX简单些。具体细节见LanguageApp_Sodino工程中的代码吧。
        
        3.3 手动刷新界面。
        要获取所有涉及到语系更新组件的索引逐一更新,体力活儿,细心点花点力气也可实现。
        
        详细实现过程见下面三个工程中:
        LanguageApp_APICheat
        LanguageApp_Reflection
        LanguageApp_Sodino
        (PS:不要问我为什么下载的工程在IDE中为什么无法直接使用,为什么打开是乱码红叉一大堆,既然是程序员,遇到问题是不是也该自己多思考思考呢。)


    注明出处:http://blog.csdn.net/sodino/article/details/6596709
  • 相关阅读:
    3.30作业
    3.30课堂
    3.29作业
    3,29课堂
    3。26作业
    3.26课堂
    3.25作业
    3.25课堂
    55、DOM与BOM的操作及事件的简介
    54、js的数据类型及对象
  • 原文地址:https://www.cnblogs.com/java20130722/p/3207340.html
Copyright © 2020-2023  润新知