不只是切换多语言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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值