android語言三

來源:互聯網
上載者:User

[Android]應用語言切換的三種方法

 

Android對國際化與多語言切換已經做得不錯了,一個應用只要命名相應語系的values-[language]檔案夾,通過“設定”→“語言&鍵盤”→“選擇語言”即可實現應用多種語言的切換。   

    但如何在應用裡自己實現?搜尋過發現網上有如下的做法:

view plaincopy to clipboardprint?

 

Resources res = getResources(); 

Configuration config = res.getConfiguration(); 

config.locale = locale; 

DisplayMetrics dm = res.getDisplayMetrics(); 

res.updateConfiguration(config, dm); 

前兩種方法的原理即在應用裡實現“選擇語言”。通過查看源碼,其核心代碼為:

 

view plaincopy to clipboardprint?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(); 

PS:感謝 曾陽 的協助。 

                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();

                }

                PS:感謝 曾陽 的協助。    可以發現IActivityManager與ActivityManagerNative都是非公開類。如何調用?第一種是API欺騙,第二種是使用Java反射機制。

    1. API欺騙

    燒制到手機中的android.jar包含了Android所需的各種類與方法;而供開發人員使用的android.jar只是其中的一部分。API欺騙是指在應用中去類比未公開的類和方法讓應用編譯通過並產生APK,然而在應用實際運行中調用的卻仍是燒制到手機中真實的android.jar。

 

通過核心代碼可以看到我們要類比的是ActivityManagerNative中的一個方法getDefault()和IActivityManager中的兩個方法getConfiguration()與updateConfiguration(config)。參照源碼,應用的工程結構圖及代碼類比如下:

 

 

代碼:

 

view plaincopy to clipboardprint?ActivityManagerNative.java 

package android.app; 

 

/**

* @author Sodino E-mail:sodinoopen@hotmail.com

* @version Time:2011-7-10上午11:37:01

*/ 

public abstract class ActivityManagerNative { 

    public static IActivityManager getDefault() { 

        return null; 

    } 

 

IActivityManager.java 

package android.app; 

 

import android.content.res.Configuration; 

import android.os.RemoteException; 

 

/**

* @author Sodino E-mail:sodinoopen@hotmail.com

* @version Time:2011-7-10上午11:37:46

*/ 

public abstract interface IActivityManager { 

    public abstract Configuration getConfiguration() throws RemoteException; 

 

    public abstract void updateConfiguration(Configuration paramConfiguration) 

            throws RemoteException; 

ActivityManagerNative.java

package android.app;

/**

* @author Sodino E-mail:sodinoopen@hotmail.com

* @version Time:2011-7-10上午11:37:01

*/

public abstract class ActivityManagerNative {

        public static IActivityManager getDefault() {

                return null;

        }

}

IActivityManager.java

package android.app;

import android.content.res.Configuration;

import android.os.RemoteException;

/**

* @author Sodino E-mail:sodinoopen@hotmail.com

* @version Time:2011-7-10上午11:37:46

*/

public abstract interface IActivityManager {

        public abstract Configuration getConfiguration() throws RemoteException;

        public abstract void updateConfiguration(Configuration paramConfiguration)

                        throws RemoteException;

}    實現類比了這兩個類後,即可正常使用上面提到的轉換語系的核心代碼了。

 

直接上代碼:

view plaincopy to clipboardprint?

private void updateLanguage(Locale locale) { 

    Log.d("ANDROID_LAB", locale.toString()); 

    try { 

        Object objIActMag, objActMagNative; 

        Class clzIActMag = Class.forName("android.app.IActivityManager"); 

        Class clzActMagNative = Class.forName("android.app.ActivityManagerNative"); 

        Method mtdActMagNative$getDefault = clzActMagNative.getDeclaredMethod("getDefault"); 

        // IActivityManager iActMag = ActivityManagerNative.getDefault();  

        objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative);  

        // Configuration config = iActMag.getConfiguration();  

        Method mtdIActMag$getConfiguration = clzIActMag.getDeclaredMethod("getConfiguration"); 

        Configuration config = (Configuration) mtdIActMag$getConfiguration.invoke(objIActMag); 

        config.locale = locale; 

        // iActMag.updateConfiguration(config);  

        // 此處需要聲明許可權:android.permission.CHANGE_CONFIGURATION  

        // 會重新調用onCreate();  

        Class[] clzParams = { Configuration.class }; 

        Method mtdIActMag$updateConfiguration = clzIActMag.getDeclaredMethod( 

                "updateConfiguration", clzParams); 

        mtdIActMag$updateConfiguration.invoke(objIActMag, config); 

    } catch (Exception e) { 

        e.printStackTrace(); 

    } 

}

        private void updateLanguage(Locale locale) {                Log.d("ANDROID_LAB", locale.toString());                try {                        Object objIActMag, objActMagNative;                        Class clzIActMag = Class.forName("android.app.IActivityManager");                       
Class clzActMagNative = Class.forName("android.app.ActivityManagerNative");                        Method mtdActMagNative$getDefault = clzActMagNative.getDeclaredMethod("getDefault");                        // IActivityManager iActMag = ActivityManagerNative.getDefault();                       
objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative);                        // Configuration config = iActMag.getConfiguration();                        Method mtdIActMag$getConfiguration = clzIActMag.getDeclaredMethod("getConfiguration");                       
Configuration config = (Configuration) mtdIActMag$getConfiguration.invoke(objIActMag);                        config.locale = locale;                        // iActMag.updateConfiguration(config);                        // 此處需要聲明許可權:android.permission.CHANGE_CONFIGURATION                       
// 會重新調用onCreate();                        Class[] clzParams = { Configuration.class };                        Method mtdIActMag$updateConfiguration = clzIActMag.getDeclaredMethod(                                        "updateConfiguration", clzParams);                       
mtdIActMag$updateConfiguration.invoke(objIActMag, config);                } catch (Exception e) {                        e.printStackTrace();                }        }    實際運行後,發現對當前系統設定了新的Locale後,不單自己的應用語系改變了,系統所有的應用語系都改變了。這肯定是不合理的。有一個解決辦法是在應用介面退出前再次對系統設定成碑的Locale,不過個人不喜歡這樣的辦法,加之調用updateConfiguration()方法後,整個Activity會重新onCreate(),這個考慮Activity的生命週期可有點費勁了。於是有了下面這第三種方法。

 

 

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是這個的格式:

view plaincopy to clipboardprint?

<string name="app_name">語言應用</string>

    <string name="app_name">語言應用</string>    對應R檔案會產生一個id指代該string

view plaincopy to clipboardprint?

public static final class string { 

    public static final int app_name=0x7f050001; 

}

    public static final class string {        public static final int app_name=0x7f050001;    }

    3.1的問題就是如何?id與string的匹配,解決方案為:

view plaincopy to clipboardprint?

Resources res = context.getResources(); 

String pkg = context.getPackageName(); 

String tag = "app_name";

int idTag = res.getIdentifier(tag, "string", pkg);

        Resources res = context.getResources();        String pkg = context.getPackageName();        String tag = "app_name";        int idTag = res.getIdentifier(tag, "string", pkg);    3.2 解析XML

    這兒要用到一個新的工具了:XmlResourceParser,解析過程有點繞,但比SAX簡單些。具體細節見LanguageApp_Sodino工程中的代碼吧。

   

    3.3 手動重新整理介面。

    要擷取所有涉及到語系更新群組件的索引逐一更新,體力活兒,細心點花點力氣也可實現。

   

    詳細實現過程見下面三個工程中:

    LanguageApp_APICheat

    LanguageApp_Reflection

    LanguageApp_Sodino

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.