Android APP切换系统语言

1.需求

  三方APP实现点击切换语言功能(类似于系统Settings中点击语言自动切换)

2.实现

   2.1 跳转到系统Settings的语言选择界面,实现功能

         跳转代码:

Intent intent = new Intent(Settings.ACTION_LOCALE_SETTINGS);

startActivity(intent);

    优点:

            最简单的方式,使用系统的功能,最方便,必然修改成功

    缺点:   

             用户体验差,跳转到Settings后跟自己app界面风格完全不搭

2.2 App中实现Settings的功能

      2.2.1 获取系统的语言设置列表数据

               先上代码再解释

import java.text.Collator;
import java.util.Locale;

public class LanguageInfoEntity implements Comparable<LanguageInfoEntity>{
    static final Collator sCollator = Collator.getInstance();

    public String label;
    public Locale locale;

    public LanguageInfoEntity(String label, Locale locale) {
        this.label = label;
        this.locale = locale;
    }

    public String getLabel() {
        return label;
    }

    public Locale getLocale() {
        return locale;
    }

    @Override
    public String toString() {
        return this.label;
    }

    @Override
    public int compareTo(LanguageInfoEntity another) {
        return sCollator.compare(this.label, another.label);
    }
}
import android.content.res.Resources;
import android.util.Log;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

public class LanguageControlImpl implements LanguageControl {
    private static final String TAG = LanguageControlImpl.class.getName();

    private static LanguageControlImpl mLanguageControlImpl;

    private LanguageControlImpl() {
    }

    public static synchronized LanguageControlImpl getInstance() {
        if (mLanguageControlImpl == null) {
            mLanguageControlImpl = new LanguageControlImpl();
        }
        return mLanguageControlImpl;
    }

    @Override
    public List<LanguageInfoEntity> getLanguageList() {
        ArrayList<String> localeList = new ArrayList<String>(Arrays.asList(
                Resources.getSystem().getAssets().getLocales()));
        String[] locales = new String[localeList.size()];
        locales = localeList.toArray(locales);
        Arrays.sort(locales);
        final int origSize = locales.length;
        Log.i(TAG, "origSize : " + origSize);
        final String[] specialLocaleCodes = {"zh_CN", "zh_TW"};
        final String[] specialLocaleNames = {"中文(简体)", "中文(繁體)"};
        Arrays.sort(locales);
        final LanguageInfoEntity[] preprocess = new LanguageInfoEntity[origSize];
        int finalSize = 0;
        for (int i = 0; i < origSize; i++) {
            final String s = locales[i];
            final int len = s.length();
            if (len == 5) {
                String language = s.substring(0, 2);
                String country = s.substring(3, 5);
                final Locale l = new Locale(language, country);

                if (finalSize == 0) {
                    Log.v(TAG, "adding initial " + toTitleCase(l.getDisplayLanguage(l)));
                    preprocess[finalSize++] =
                            new LanguageInfoEntity(toTitleCase(l.getDisplayLanguage(l)), l);
                } else {
                    // check previous entry:
                    //  same lang and a country -> upgrade to full name and
                    //    insert ours with full name
                    //  diff lang -> insert ours with lang-only name
                    if (preprocess[finalSize - 1].locale.getLanguage().equals(
                            language) &&
                            !preprocess[finalSize - 1].locale.getLanguage().equals("zz")) {
                        Log.v(TAG, "backing up and fixing " +
                                preprocess[finalSize - 1].label + " to " +
                                getDisplayName(preprocess[finalSize - 1].locale,
                                        specialLocaleCodes, specialLocaleNames));
                        preprocess[finalSize - 1].label = toTitleCase(
                                getDisplayName(preprocess[finalSize - 1].locale,
                                        specialLocaleCodes, specialLocaleNames));
                        Log.v(TAG, "  and adding " + toTitleCase(
                                getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
                        preprocess[finalSize++] =
                                new LanguageInfoEntity(toTitleCase(
                                        getDisplayName(
                                                l, specialLocaleCodes, specialLocaleNames)), l);
                    } else {
                        String displayName;
                        if (s.equals("zz_ZZ")) {
                            displayName = "[Developer] Accented English";
                        } else if (s.equals("zz_ZY")) {
                            displayName = "[Developer] Fake Bi-Directional";
                        } else {
                            displayName = toTitleCase(l.getDisplayLanguage(l));
                        }
                        Log.v(TAG, "adding " + displayName);
                        preprocess[finalSize++] = new LanguageInfoEntity(displayName, l);
                    }
                }
            }
        }

        final LanguageInfoEntity[] localeInfos = new LanguageInfoEntity[finalSize];
        for (int i = 0; i < finalSize; i++) {
            localeInfos[i] = preprocess[i];
        }

        List<LanguageInfoEntity> resultList = new ArrayList<LanguageInfoEntity>();
        //如果无法获取列表,那就是这里的有问题,在排序的时候异常,注释掉也可以
        Arrays.sort(localeInfos);
        for (LanguageInfoEntity localeInfo : localeInfos) {
            if (!localeInfo.getLabel().isEmpty()) {
                resultList.add(localeInfo);
            }
        }
        return resultList;
    }

    private static String toTitleCase(String s) {
        if (s.length() == 0) {
            return s;
        }

        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
    }

    private static String getDisplayName(
            Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
        String code = l.toString();

        for (int i = 0; i < specialLocaleCodes.length; i++) {
            if (specialLocaleCodes[i].equals(code)) {
                return specialLocaleNames[i];
            }
        }

        return l.getDisplayName(l);
    }
}

       通过getLanguageList方法我们获取到了一个list,这个list里存放的就是多国语言对应信息,后面会用到。这里需要注意的是又不断的Arrays.sort(…)在排序,这里的排序结果是根据语言来的,比如说在英文状态下就是a开头的排在前面,但是在中文状态下,就是中文(简体)在队列的最前面。

       2.2.2 展示和点击相应

      有了列表,展示无非就是RecycleView显示,这里不做赘述,下面讲一下点击了某个语言,响应部分代码

public void onItemClick(LocaleInfoBean localeInfoBean) {
        try {
            Class classActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
            Method getDefault = classActivityManagerNative.getDeclaredMethod("getDefault");
            Object objIActivityManager = getDefault.invoke(classActivityManagerNative);
            Class classIActivityManager = Class.forName("android.app.IActivityManager");
            Method getConfiguration = classIActivityManager.getDeclaredMethod("getConfiguration");


            Configuration config = (Configuration) getConfiguration.invoke(objIActivityManager);
            config.setLocale(localeInfoBean.getLocale());
            //config.userSetLocale = true;
            Class clzConfig = Class
                    .forName("android.content.res.Configuration");
            java.lang.reflect.Field userSetLocale = clzConfig
                    .getField("userSetLocale");
            userSetLocale.set(config, true);
            Class[] clzParams = {Configuration.class};
            Method updateConfiguration = classIActivityManager.getDeclaredMethod("updateConfiguration", clzParams);
            updateConfiguration.invoke(objIActivityManager, config);


            BackupManager.dataChanged("com.android.providers.settings");
        } catch (Exception e) {
            Log.i(TAG, "changeSystemLanguage: " + e.getLocalizedMessage());
            e.printStackTrace();
        }
    }

这里要说明下,这个修改方式是针对6.0以前版本的,6.0以后的版本,更新语言接口有修改,必须要注意。

想要修改语言不仅要有代码,而且还要加上对应的权限,权限代码如下

coreApp="true"
android:sharedUserId="android.uid.system"
android:configChanges="keyboardHidden|orientation|screenSize"
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />

如果没有系统签名,应该是无法修改语言的,直接报错

没必要在configchanges里加入“locale|layoutDirection”

 

特别注意:

       现在特别流行的Android MVVM框架,如果你使用了databinding那套,那么可能会发生一种情况,一个activity中既有一部分固定view,又有一个fragment。fragment展示语言列表并响应语言切换,但是fragment显示了新切换的语言,但是activity中固定的view不会跟着刷新,而activity的生命周期已经重新执行过一遍了,这个强制finish也不会刷新activity中固定的view 显示的子串的。

       系统语言已经成功切换了,但是view不刷新,这个不是语言设置问题,如果你没有采用databinding那套,activity中固定view是可以正常刷新显示对应语言字串的,应该是databinding持久化了数据,导致没有刷新,框架问题,无解。

       那么我们可以换一个规避方案,在View的Text部分,也采用对应的观察者方式即:

   android:text="@{context.getResources().getString(viewModel.test)}"

       Activity刷新生命周期的时候,在ViewModle里直接set一个String对应的resid即可,引发数据的变化,重新引用就可以了。

public final ObservableInt test = new ObservableInt(R.string.xxx);
​​​​​​​test.set(R.string.xxx);

 

 

    原文作者:dzy_mails
    原文地址: https://blog.csdn.net/dzy_mails/article/details/100010037
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞