Android 仿微信多语言切换

作者: 斌林诚上 | 来源:发表于2019-03-09 22:26 被阅读1次
image

一、简介
二、效果预览
三、实现步骤
1、功能实现
2、配置多语言文件
四、MultiLanguageUtils分析
五、Demo地址
六、内容推荐


一、简介

我想搜到这篇文章的朋友应该不需要我多介绍,也明白多语言是用来干嘛。。。

当一个应用越做越大之后,用的人越来越多,要满足不同的群体所以都会有这一功能。

不过你会发现越到后面添加这功能会比较麻烦。所以前期做好准备为佳。

不扯这乱七八糟的东西了。。。。亮剑吧!!!

image

二、效果预览

是不是要先看看大招是咋样的,万一是花拳绣腿。不满意,就浪费大家时间了。

image

大家可可能会奇怪,不是切换成英文了吗? 怎么还有中文字体。

其实我只是对首页的 标题底部导航栏 做了处理 其他的没有变

所以大家看效果的话 只需观察 那两处地方即可。

如果对这个功能有兴趣可以继续往下观察。。没兴趣的也请往下看 ,看完可能会更没兴趣。哈哈 开个玩笑

image

三、实现步骤

1、功能实现

布局我就不解释了,就随便搭搭。源码地址就贴在最下面,想copy的同学自己去下载哈。对了,如果不错。别忘了 Star

主要的功能就在于当我选中某个语言时,点击保存(上图是“打钩”的图片)的时候去实现语言的切换。 那么,是怎么实现的呢?预知后事如何且看下文分解!!

上菜:点击保存执行事假

     //区分是选中哪种语言
    switch (datas.get(checkPos)) {
        case "跟随系统"://切换到 跟随系统
            //获取手机系统语言
            Locale locale = MultiLanguageUtils.getSystemLanguage().get(0);
            String language = locale.getLanguage();
            String country = locale.getCountry();
            //切换成手机系统语言  例:手机系统是中文则换成中文
            MultiLanguageUtils.changeLanguage(activity,language, country);
            //清空SP数据 ,用于当系统切换语言时 应用可以同步保持切换 例:系统转换成英文 则应用语言也会变成英文
            MultiLanguageUtils.changeLanguage(activity,null,null);
             break;
        case "简体中文":// 切换到 中文
            MultiLanguageUtils.changeLanguage(activity, "zh", "ZH");
            break;
        case "English"://切换到 英文
            MultiLanguageUtils.changeLanguage(activity, "en", "US");
            break;
        default://默认切换成中文
            MultiLanguageUtils.changeLanguage(activity, "zh", "ZH");
            break;
    }
    //关闭应用所有Activity
    //AppManager.getAppManager().finishAllActivity();
    //启动 MainActivity
    //IntentUtils.toActivity(activity, MainActivity.class,true);
image.gif

上面的切换语言功能都已经封装成一个工具类,当需要执行切换语言的时候 只需调用一行代码即可。是不是很兴奋 ,其实更麻烦的在后面,先安慰一下大家。

要让切换语言起效果,还需要重启APP。

例:上面代码是先调用了个人封装的一个Activity管理类来关闭所有的页面,然后再重启MainActivity。大家可以根据情况自己写,也可以到源码地址找到相关工具类拿去用。

大餐来了 MultiLanguageUtils.class 先感谢下大神提供的这个类:https://blog.csdn.net/m0_38074457/article/details/84993366

个人做了调整,改的有点面目全非,现在分享给大家。如果还行。。。你们懂得 点个赞意思一下就行 ,,关注,分享,打赏 我是不敢想的。

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.LocaleList;
import android.support.v4.os.ConfigurationCompat;
import android.support.v4.os.LocaleListCompat;
import android.text.TextUtils;
import android.util.DisplayMetrics;

import java.util.Locale;

import blcs.lwb.lwbtool.Constants;

/**
 * Todo 多语言设置
 * 来自:https://blog.csdn.net/m0_38074457/article/details/84993366
 * 使用步骤:
 * 1、Application中onCreate添加registerActivityLifecycleCallbacks(MultiLanguageUtils.callbacks);
         @Override
         protected void attachBaseContext(Context base) {
         //系统语言等设置发生改变时会调用此方法,需要要重置app语言
         super.attachBaseContext(MultiLanguageUtils.attachBaseContext(base));
         }
 * 2、改变应用语言调用MultiLanguageUtils.changeLanguage(activity,type,type);
 */
//public final static String SP_LANGUAGE="SP_LANGUAGE";
//public final static String SP_COUNTRY="SP_COUNTRY";
public class MultiLanguageUtils {
    /**
     * TODO 1、 修改应用内语言设置
     * @param language    语言  zh/en
     * @param area      地区
     */
    public static void changeLanguage(Context context,String language, String area) {
        if (TextUtils.isEmpty(language) && TextUtils.isEmpty(area)) {
            //如果语言和地区都是空,那么跟随系统s
            SPUtils.put(context, Constants.SP_LANGUAGE,"");
            SPUtils.put(context, Constants.SP_COUNTRY,"");
        } else {
            //不为空,那么修改app语言,并true是把语言信息保存到sp中,false是不保存到sp中
            Locale newLocale = new Locale(language, area);
            setAppLanguage(context,newLocale);
            saveLanguageSetting(context, newLocale);
        }
    }

    /**
     * Todo 更新应用语言
     * @param context
     * @param locale
     */
    private static void setAppLanguage(Context context, Locale locale) {
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        Configuration configuration = resources.getConfiguration();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(locale);
            configuration.setLocales(new LocaleList(locale));
            context.createConfigurationContext(configuration);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLocale(locale);
            resources.updateConfiguration(configuration,metrics);
        } else {
            configuration.locale = locale;
            resources.updateConfiguration(configuration,metrics);
        }
    }

    /**
     * TODO 3、 跟随系统语言
     */
    public static Context attachBaseContext(Context context) {
        String spLanguage = (String) SPUtils.get(context, Constants.SP_LANGUAGE,"");
        String spCountry = (String) SPUtils.get(context, Constants.SP_COUNTRY,"");
        if(!TextUtils.isEmpty(spLanguage)&&!TextUtils.isEmpty(spCountry)){
            Locale  locale = new Locale(spLanguage, spCountry);
            setAppLanguage(context, locale);
        }
        return context;
    }

    /**
     * 判断sp中和app中的多语言信息是否相同
     */
    public static boolean isSameWithSetting(Context context) {
        Locale locale = getAppLocale(context);
        String language = locale.getLanguage();
        String country = locale.getCountry();
        String sp_language = (String) SPUtils.get(context, Constants.SP_LANGUAGE,"");
        String sp_country = (String) SPUtils.get(context, Constants.SP_COUNTRY,"");
        if (language.equals(sp_language) && country.equals(sp_country)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 保存多语言信息到sp中
     */
    public static void saveLanguageSetting(Context context, Locale locale) {
        SPUtils.put(context, Constants.SP_LANGUAGE,locale.getLanguage());
        SPUtils.put(context, Constants.SP_COUNTRY,locale.getCountry());
    }

    /**
     * 获取应用语言
     */
    public static Locale getAppLocale(Context context){
        Locale local;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            local =context.getResources().getConfiguration().getLocales().get(0);
        } else {
            local =context.getResources().getConfiguration().locale;
        }
      return local;
    }

    /**
     * 获取系统语言
     */
    public static LocaleListCompat getSystemLanguage(){
        Configuration configuration = Resources.getSystem().getConfiguration();
        LocaleListCompat locales = ConfigurationCompat.getLocales(configuration);
      return locales;
    }

    //注册Activity生命周期监听回调,此部分一定加上,因为有些版本不加的话多语言切换不回来
    //registerActivityLifecycleCallbacks(callbacks);
    public static  Application.ActivityLifecycleCallbacks callbacks = new Application.ActivityLifecycleCallbacks() {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            String language = (String) SPUtils.get(activity, Constants.SP_LANGUAGE,"");
            String country = (String) SPUtils.get(activity, Constants.SP_COUNTRY,"");
            LogUtils.e(language);
            if (!TextUtils.isEmpty(language) && !TextUtils.isEmpty(country)) {
                //强制修改应用语言
                if (!isSameWithSetting(activity)) {
                    Locale locale = new Locale(language, country);
                    setAppLanguage(activity,locale);
                }
            }
        }

        @Override
        public void onActivityStarted(Activity activity) {

        }

        @Override
        public void onActivityResumed(Activity activity) {

        }

        @Override
        public void onActivityPaused(Activity activity) {

        }

        @Override
        public void onActivityStopped(Activity activity) {

        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

        }

        @Override
        public void onActivityDestroyed(Activity activity) {

        }
    };

}

这里可以能还需要一个封装的SP工具

import android.content.Context;
import android.content.SharedPreferences;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
/**
 * SharedPreferences的封装
 */
public class SPUtils
{
    public SPUtils()
    {
        /* cannot be instantiated */
        throw new UnsupportedOperationException("cannot be instantiated");
    }

    /**
     * 保存在手机里面的文件名
     */
    public static final String FILE_NAME = "share_data";

    /**
     * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
     *
     * @param context
     * @param key
     * @param object
     */
    public static void put(Context context, String key, Object object)
    {

        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();

        if (object instanceof String)
        {
            editor.putString(key, (String) object);
        }
        else if (object instanceof Integer)
        {
            editor.putInt(key, (Integer) object);
        }
        else if (object instanceof Boolean)
        {
            editor.putBoolean(key, (Boolean) object);
        }
        else if (object instanceof Float)
        {
            editor.putFloat(key, (Float) object);
        }
        else if (object instanceof Long)
        {
            editor.putLong(key, (Long) object);
        }
        else
        {
            editor.putString(key, object.toString());
        }

        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
     *
     * @param context
     * @param key
     * @param defaultObject
     * @return
     */
    public static Object get(Context context, String key, Object defaultObject)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);

        if (defaultObject instanceof String)
        {
            return sp.getString(key, (String) defaultObject);
        }
        else if (defaultObject instanceof Integer)
        {
            return sp.getInt(key, (Integer) defaultObject);
        }
        else if (defaultObject instanceof Boolean)
        {
            return sp.getBoolean(key, (Boolean) defaultObject);
        }
        else if (defaultObject instanceof Float)
        {
            return sp.getFloat(key, (Float) defaultObject);
        }
        else if (defaultObject instanceof Long)
        {
            return sp.getLong(key, (Long) defaultObject);
        }

        return null;
    }

    /**
     * 移除某个key值已经对应的值
     *
     * @param context
     * @param key
     */
    public static void remove(Context context, String key)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.remove(key);
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 清除所有数据
     *
     * @param context
     */
    public static void clear(Context context)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.clear();
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 查询某个key是否已经存在
     *
     * @param context
     * @param key
     * @return
     */
    public static boolean contains(Context context, String key)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        return sp.contains(key);
    }

    /**
     * 返回所有的键值对
     *
     * @param context
     * @return
     */
    public static Map<String, ?> getAll(Context context)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        return sp.getAll();
    }

    /**
     * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类
     * @author zhy
     *
     */
    private static class SharedPreferencesCompat
    {
        private static final Method sApplyMethod = findApplyMethod();

        /**
         * 反射查找apply的方法
         * @return
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private static Method findApplyMethod()
        {
            try
            {
                Class clz = SharedPreferences.Editor.class;
                return clz.getMethod("apply");
            } catch (NoSuchMethodException e)
            {
            }

            return null;
        }

        /**
         * 如果找到则使用apply执行,否则使用commit
         *
         * @param editor
         */
        public static void apply(SharedPreferences.Editor editor)
        {
            try
            {
                if (sApplyMethod != null)
                {
                    sApplyMethod.invoke(editor);
                    return;
                }
            } catch (IllegalArgumentException e)
            {
            } catch (IllegalAccessException e)
            {
            } catch (InvocationTargetException e)
            {
            }
            editor.commit();
        }
    }
}

如果你们以为有了这几个工具就可以出去吹牛逼,那么是你们天真了,而不是我想的太多。还有更麻烦的事情还没开始呢

在继承Application的类中onCreate方法内添加监听回调

 @Override
 public void onCreate() {
    super.onCreate();
    //多语言设置
    registerActivityLifecycleCallbacks(MultiLanguageUtils.callbacks);
}

跟随系统语言也要在Application中的attachBaseContext配置(感觉可以去掉,本人亲测根本没有调用到。不知道是不是不同机型的原因。先保留着 ,后续若发现无用,可删掉。写出来主要是钱作者有写但描述不够详细)

    @Override
    protected void attachBaseContext(Context base) {
        //系统语言等设置发生改变时会调用此方法,需要要重置app语言
        super.attachBaseContext(MultiLanguageUtils.attachBaseContext(base));
    }

2、配置多语言文件

最麻烦的工作来了,若前期没做好相关工作。那么你可以开始跟我一步步来实现了

1、新增多语言文件

image

可以手动创建,但要符合命名规范。(不推荐)

这里介绍androidStudio 新建多语言文本方法

image

右击res——New——Android resource file

image image

创建完文件后。。。剩下就是抽出文本进行翻译了。。

2、把项目中的文本都写到strings.xml文件中

image

像我这样:

image

如何使用这些文本?

UI中: android:text="@string/Title"

代码中:context.getString(R.string.Title)

快捷键使用:选中文本使用 Alt + Enter 打开Extract string resource

image image

给文本命名 ——> 打钩所有的values——>点击OK 就可以自动生成到strings文件里面

接着翻译 values-en 文件中的文本。

image

到这里差不多都介绍完了 ,若有代码遗落可查看源码 。毕竟已经实现过了 ,拿出来分析给大家,可能会有疏忽。

image

MultiLanguageUtils只加了简单注释,若对MultiLanguageUtils有什么不理解的地方 请继续往下看。

四、MultiLanguageUtils分析

我们切换语言只需调用:MultiLanguageUtils.changeLanguage(activity, "zh", "ZH");

1、changeLanguage(Context context,String language, String area):

 public static void changeLanguage(Context context,String language, String area) {
        if (TextUtils.isEmpty(language) && TextUtils.isEmpty(area)) {
            //如果语言和地区都是空,那么跟随系统s
            SPUtils.put(context, Constants.SP_LANGUAGE,"");
            SPUtils.put(context, Constants.SP_COUNTRY,"");
        } else {
            //不为空,那么修改app语言,并true是把语言信息保存到sp中,false是不保存到sp中
            Locale newLocale = new Locale(language, area);
            setAppLanguage(context,newLocale);
            saveLanguageSetting(context, newLocale);
        }
    }

首先判断language,area 是否为空

空:清空app本地存储多语言的sp,为什么要清空呢?

当启动APP时会回调 registerActivityLifecycleCallbacks(MultiLanguageUtils.callbacks);

该方法内部会绑定生命周期调用如下方法

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            String language = (String) SPUtils.get(activity, Constants.SP_LANGUAGE,"");
            String country = (String) SPUtils.get(activity, Constants.SP_COUNTRY,"");
            LogUtils.e(language);
            if (!TextUtils.isEmpty(language) && !TextUtils.isEmpty(country)) {
                //强制修改应用语言
                if (!isSameWithSetting(activity)) {
                    Locale locale = new Locale(language, country);
                    setAppLanguage(activity,locale);
                }
            }
        }

若sp有值则根据对应的值设置APP语言 ,若为空 ,则会默认取系统语言。

注:当选中“跟随系统语言”时,若不清空SP。则会造成当系统语言改变,App还是会根据SP的值来设置语言。

有值:根据传入的language,area 值切换成相对应的语言

2、setAppLanguage(Context context, Locale locale)

/**
     * Todo 更新应用语言
     * @param context
     * @param locale
     */
    private static void setAppLanguage(Context context, Locale locale) {
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        Configuration configuration = resources.getConfiguration();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(locale);
            configuration.setLocales(new LocaleList(locale));
            context.createConfigurationContext(configuration);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLocale(locale);
            resources.updateConfiguration(configuration,metrics);
        } else {
            configuration.locale = locale;
            resources.updateConfiguration(configuration,metrics);
        }
    }

更改应用的语言主要还是通过更改App配置来实现,主要实现步骤如下:

1、先获取资源配置对象 2、更改资源对象属性 3、更新配置信息

多语言语言适配了Android N (7.0)

7.0以前更新配置信息使用

resources.updateConfiguration(configuration, metrics);

7.0以后更新配置信息使用

context.createConfigurationContext(configuration);

3、跟随系统语言实现思路分析

1、当保存跟随系统语言时,获取系统语言 ,并更新App语言。

2、当更改手机设置语言后,启动App的时候重新更改App语言(保存其它语言信息的时候,启动App是时候也需要根据保存信息重新设置)

这里主要分析第二点。第一点只要调用changeAppLanguage 就行了

当App启动的时候会回调registerActivityLifecycleCallbacks(MultiLanguageUtils.callbacks);

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            String language = (String) SPUtils.get(activity, Constants.SP_LANGUAGE,"");
            String country = (String) SPUtils.get(activity, Constants.SP_COUNTRY,"");
            LogUtils.e(language);
            if (!TextUtils.isEmpty(language) && !TextUtils.isEmpty(country)) {
                //强制修改应用语言
                if (!isSameWithSetting(activity)) {
                    Locale locale = new Locale(language, country);
                    setLanguage(activity,locale);
                }
            }
        }

也是通过Sp是否有保存信息来设置系统语言。

终于写完了,看了一下篇幅还是挺长,容我感慨一下。又熬夜了 ,大家要手下留

image

好吧!!! 再见

五、Demo地址

https://github.com/DayorNight/BLCS

六、内容推荐

CSDN:https://blog.csdn.net/cs_lwb/article/details/88051709

《Android 仿微信全局字体大小调整》

《Android JUnit单元测试》

《Android Log日志封装》

《Android Rxjava+Retrofit网络请求框架封装(一)》

如果你觉得我写的不错或者对您有所帮助的话

不妨顶一个【微笑】,别忘了点赞、收藏、加关注哈

看在我花了这么多时间整理写成文章分享给大家的份上,记得手下留情哈

您的每个举动都是对我莫大的支持

image

相关文章

网友评论

    本文标题:Android 仿微信多语言切换

    本文链接:https://www.haomeiwen.com/subject/kvryuqtx.html