接着前面的内容往下走不只是切换多语言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