不只是切换多语言Android

Posted vv_小虫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不只是切换多语言Android相关的知识,希望对你有一定的参考价值。

接着前面的内容往下走不只是切换多语言Android(一),我们来实现一下android app内切换语言。

思路一:

1、把所有的activity用一个集合装起来
2、改变app的语言环境
3、干掉所有的activity,然后进入重新启动app

思路二:

1、获取所有需要改变语言的组件
2、改变app的语言环境
3、通知所有的组件重新加载text

两种方式比较,如果跟偏重程序性能的话,用第一种方案(微信毛貌似用的就是这种),如果偏重用户体验不重启app的话,就用第二种方案。

在此之前呢,我们先了解下观察者模式,不了解没关系,我们通过案例说明一下~~

1、(被观察的事物)我们这里的被观察的对象就是修改语言了,于是我们把修改语言这个subject抽取出来:
LanguageSubject.java

public abstract class LanguageSubject 
    private List<LanguageObserver> observers;

    public synchronized void attach(LanguageObserver observer) 
        if (observers == null) 
            observers = new ArrayList<>();
        
        observers.add(observer);
    

    public synchronized void dettach(LanguageObserver observer) 
        if (observers == null || observers.size() == 0 || !observers.contains(observer)) return;
        observers.remove(observer);
    

    protected void notifyChangeLanguage(String language) throws Exception 
        if (observers == null || observers.size() == 0) return;
        for (LanguageObserver observer :
                observers) 
            observer.update(language);
        
    

    public interface IChangeSuccessCallBack 
        void success();

        void erro();
    

里面方法定义很简单,包括:attach(添加需要通知的对象)、dettach(移除通知对象)、notifyChangeLanguage(通知所有的对象“我需要修改语言了“)。

2、(需要通知的对象LanguageObserver)

/**
 * Created by leo on 17/4/6.
 */

public interface LanguageObserver 
    void update(String language) throws Exception;

当接到通知的时候,需要做出相应的动作,update方法即为做出的响应。

3、说说我们具体的主题事件类了(修改语言)

package com.yasin.library;

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.util.Log;

import com.yasin.library.util.LANGUAGE;
import com.yasin.library.util.LanguageSpUtil;

import java.util.Locale;

/**
 * Created by leo on 17/4/6.
 */

public class LanguageManage extends LanguageSubject 
    private static final String TAG = "LanguageManage";
    private static LanguageManage instance;
    private Context context;

    private LanguageManage(Context context) 
        this.context = context;
    

    public static synchronized LanguageManage getInstance(Context context) 
        if (instance == null) 
            instance = new LanguageManage(context);
        
        return instance;
    

    public void change(LANGUAGE language, IChangeSuccessCallBack callBack) 
        try 
            // 应用用户选择语言
            if (language == LANGUAGE.ZH) 
                change(language, Locale.CHINESE);
                if(callBack!=null)callBack.success();
             else if (language == LANGUAGE.EN) 
                change(language, Locale.ENGLISH);
                if(callBack!=null)callBack.success();
             else 
                if (callBack != null) callBack.erro();
                Log.e(TAG, "Language: " + language.toString() + " is not supported!");
            
         catch (Exception e) 
            e.printStackTrace();
            if (callBack != null) callBack.erro();
        
    

    private void change(LANGUAGE language, Locale locale) throws Exception 
        Resources resources = context.getResources();
        DisplayMetrics dm = resources.getDisplayMetrics();
        Configuration config = resources.getConfiguration();
        config.locale = locale;
        resources.updateConfiguration(config, dm);
        notifyChangeLanguage(language.toString());
        LanguageSpUtil.saveLanguage(context, language);
    

LanguageManage是继承了LanguageSubject主题。

好啦~观察者模式就完啦~! 是不是觉得很简单呢?

我们也跟AppCompatActivity一样,创建一个LanguageDelegate(为我们的观察者跟inflator 的factory)需要实现LanguageObserver跟LayoutInflaterFactory:

package com.yasin.library;

import android.app.Activity;
import android.content.Context;
import android.support.v4.view.LayoutInflaterFactory;

/**
 * Created by leo on 17/4/6.
 */

public abstract class LanguageDelegate implements LanguageObserver, LayoutInflaterFactory 
    protected Context mContext;
    public static LanguageDelegate create(Activity activity) 
        return new LanguageDelegateBaseImp(activity);
    

    public abstract void installViewFactory();

    public void onResume() 
        if(mContext==null)return;
        LanguageManage.getInstance(mContext).attach(this);
    

    public void onDestory() 
        if(mContext==null)return;
        LanguageManage.getInstance(mContext).dettach(this);
    

在onResume方法的时候,把我们的观察者对象装进主题事件中:

public void onResume() 
        if(mContext==null)return;
        LanguageManage.getInstance(mContext).attach(this);
    

然后在onDestory的时候把我们的观察者对象移除被观察者事物:

public void onDestory() 
        if(mContext==null)return;
        LanguageManage.getInstance(mContext).dettach(this);
    

LanguageDelegateBaseImp具体的观察者对象(具体实现我们切换语言):

package com.yasin.library;

import android.content.Context;
import android.support.v4.view.LayoutInflaterCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;

import com.yasin.library.exception.LanguageException;
import com.yasin.library.widge.LanguageButton;
import com.yasin.library.widge.LanguageEditText;
import com.yasin.library.widge.LanguageSupportable;
import com.yasin.library.widge.LanguageTextView;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by leo on 17/4/6.
 */

public class LanguageDelegateBaseImp extends LanguageDelegate 
    private static final String TAG = "LanguageDelegateBaseImp";
    private List<WeakReference<LanguageSupportable>> mSupports;

    public LanguageDelegateBaseImp(Context context) 
        this.mContext = context;
        mSupports = new ArrayList<>();
    

    @Override
    public void installViewFactory() 
        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        if (layoutInflater.getFactory() == null) 
            LayoutInflaterCompat.setFactory(layoutInflater, this);
         else 
            if (!(LayoutInflaterCompat.getFactory(layoutInflater)
                    instanceof LanguageDelegateBaseImp)) 
                Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                        + " so we can not install LanguageCompat's");
            
        
    

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) 
        if ("TextView".equals(name)) 
            LanguageTextView supportable = new LanguageTextView(context, attrs);
            mSupports.add(new WeakReference<LanguageSupportable>(supportable));
            return supportable;
         else if ("Button".equals(name)) 
            LanguageButton supportable = new LanguageButton(context, attrs);
            mSupports.add(new WeakReference<LanguageSupportable>(supportable));
            return supportable;
         else if ("EditText".equals(name)) 
            LanguageEditText supportable = new LanguageEditText(context, attrs);
            mSupports.add(new WeakReference<LanguageSupportable>(supportable));
            return supportable;
        
        return null;
    

    @Override
    public void update(String language) throws Exception 
        try 
            for (WeakReference<LanguageSupportable> support : mSupports) 
                LanguageSupportable s = support.get();
                s.change();
            
         catch (Exception e) 
            throw new LanguageException(e.getMessage(), e.getCause());
        
    

在具体的观察者对象LanguageDelegateBaseImp中,当我们的infaltor.inflate的时候(加载xml文件),就会根据组件的名字创建对应的组件:

package com.yasin.library.widge;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;

import com.yasin.library.helper.TextSupportHelper;
import com.yasin.library.helper.TextSupportHelperImp;

/**
 * Created by leo on 17/4/6.
 */

public class LanguageTextView extends TextView implements LanguageSupportable 
    private TextSupportHelper textSupportHelper;

    public LanguageTextView(Context context, AttributeSet attrs) 
        super(context, attrs);
        initViews(attrs, 0);
    

    public LanguageTextView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        initViews(attrs, defStyleAttr);
    

    private void initViews(AttributeSet attrs, int defStyleAttr) 
        textSupportHelper = new TextSupportHelperImp(this, attrs, defStyleAttr);
    

    @Override
    public void change() 
        textSupportHelper.apply();
    

比如TextView,然后当textSupportHelper帮我们实现切换text文字:

package com.yasin.library.helper;

import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.TextView;

import com.yasin.library.R;

/**
 * Created by leo on 17/4/7.
 */

public class TextSupportHelperImp extends TextSupportHelper 
    /**
     * TextView的string的资源id
     */
    protected int resourceId;
    /**
     * 当前的TextView
     */
    protected TextView textView;

    public int getResourceId() 
        return resourceId;
    

    public void setResourceId(int resourceId) 
        this.resourceId = resourceId;
    

    public TextSupportHelperImp(TextView textView, AttributeSet attrs, int defStyleAttr) 
        this.textView = textView;
        //获取自定义属性
//        <?xml version="1.0" encoding="utf-8"?>
//        <resources>
//        <declare-styleable name="LanguageHelper">
//        <attr name="android:text" />
//        <attr name="android:hint" />
//        </declare-styleable>
//        </resources
        TypedArray a = textView.getContext().obtainStyledAttributes(attrs, R.styleable.LanguageHelper, defStyleAttr, 0);
        try 
            //获取到text的资源路径
            resourceId = a.getResourceId(R.styleable.LanguageHelper_android_text, 0);
         catch (Exception e) 
         finally 
            a.recycle();
        
    

    @Override
    public void apply() 
        //判断当前资源路径是否有效
        //因为我们在xml中可能是直接设置的文字,就无法国际化了
        int id = checkResourceId(resourceId);
        if (id != INVALID_ID) 
            try 
                textView.setText(textView.getContext().getResources().getString(id));
             catch (Exception e) 

            
        
    

好啦!!差不多定义完了,怎么用呢??

首先创建两个value文件夹(我这里就是英文跟中文),然后里面放对应的string文件:

values-en:

<resources>
    <string name="app_name">LanguageSupport</string>
    <string name="hello">Hello LanguageSupport!</string>
    <string name="switch_language">switch language</string>
    <string name="change_succ">change success!</string>
</resources>

values-zh:

<resources>
    <string name="app_name">语言切换</string>
    <string name="hello">你好 !</string>
    <string name="switch_language">切换语言</string>
    <string name="change_succ">修改成功!</string>

</resources>

跟AppCompatActivity一样,创建一个BaseActivity:

package com.yasin.languagesupport;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.view.LayoutInflaterCompat;
import android.support.v4.view.LayoutInflaterFactory;
import android.support.v7.app.AppCompatActivity;

import com.yasin.library.LanguageDelegate;

/**
 * Created by leo on 17/4/6.
 */

public class BaseActivity extends AppCompatActivity
    private LanguageDelegate mDelegate;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        LayoutInflaterCompat.setFactory(getLayoutInflater(),getLanguageDelegate());
        super.onCreate(savedInstanceState);
        ActivityManager.addActivity(this);
    
    private LayoutInflaterFactory getLanguageDelegate() 
        return mDelegate=LanguageDelegate.create(this);
    

    @Override
    protected void onResume() 
        super.onResume();
        mDelegate.onResume();
    

    @Override
    protected void onDestroy() 
        super.onDestroy();
        mDelegate.onDestory();
        ActivityManager.remove(this);
    



然后就可以用了:

package com.yasin.languagesupport;

import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;

import com.yasin.library.LanguageManage;
import com.yasin.library.LanguageSubject;
import com.yasin.library.util.LANGUAGE;
import com.yasin.library.util.LanguageSpUtil;

public class MainActivity extends BaseActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setTitle(R.string.app_name);
        setContentView(R.layout.activity_main);
    

    public void change(View view) 
        String language = LanguageSpUtil.getLanguage(this);
        LANGUAGE lan;
        if(TextUtils.isEmpty(language))
            language= LANGUAGE.ZH.toString();
        
        if(LANGUAGE.EN.toString().equals(language))
            lan=LANGUAGE.ZH;
        else
            lan=LANGUAGE.EN;
        
        LanguageManage.getInstance(this).change(lan, new LanguageSubject.IChangeSuccessCallBack() 
            @Override
            public void success() 
                Toast.makeText(getApplicationContext(),getString(R.string.change_succ),Toast.LENGTH_SHORT).show();
                setTitle(R.string.app_name);
//                startActivity(new Intent(MainActivity.this,SecondActivity.class));
            
            @Override
            public void erro() 

            
        );
    

好啦~~还是不懂的童鞋可以直接拖代码运行一下应该就会明白了,不懂的也可以找我哈!!

最后附上项目github的链接:
https://github.com/913453448/LanguageSupport

以上是关于不只是切换多语言Android的主要内容,如果未能解决你的问题,请参考以下文章

Android国际化多语言切换

Android国际化多语言切换

Android app应用多语言切换功能实现

网站多语言切换插件

iOS多语言-跟随系统/应用内切换

Android 应用内多语言切换