android 语音适配,Android多语言适配繁体中文

Android多语言适配一般默认是values/drawable目录下的文件是英文语言的资源,如果在中文下使用不同的资源则需要新建values-zh/drawable-zh-xxxdpi这样,res目录下的命名规则是drawable-语言-国家-xxxdpi,在没有特定国家的资源会先找语言相关的目录。但是中文有点特别!这也是一个坑,按常规思维drawable-zh应该是简体中文/繁体中文共有的资源,就是如果没有drawable-zh-rTw或drawable-zh-rHK的资源的话当切到繁体中文时会去drawable-zh查找资源,这样想是大错特错,实际的结果是没有繁体资源时系统会去默认的目录下查找,也就是在drawable或drawable-xxxdpi这些目录找,这个是android的bug还是故意认为HK和TW不是我大中华的?

废话不多话说,实际开发过程中会有这样的需求,就是希望所有的中文不管是繁体还是简体都是共用资源的,根据上面说的规则,如果不分别在zh-rTW和zh-rHK都放入资源的话是不可能做到了,那如果还是想省点资源怎么办呢?下面就是说说图片资源如何做到这点,这里关键要了解android资源的实现,android资源像ImageView查找资源是通过Resources来操作的,来看ImageView查找图片时的关键函数:

private void resolveUri() {

if (mDrawable != null) {

return;

}

if (getResources() == null) {

return;

}

Drawable d = null;

if (mResource != 0) {

try {

// 关键的地方

d = mContext.getDrawable(mResource);

} catch (Exception e) {

Log.w(LOG_TAG, "Unable to find resource: " + mResource, e);

// Don't try again.

mResource = 0;

}

} else if (mUri != null) {

d = getDrawableFromUri(mUri);

if (d == null) {

Log.w(LOG_TAG, "resolveUri failed on bad bitmap uri: " + mUri);

// Don't try again.

mUri = null;

}

} else {

return;

}

}

updateDrawable(d);

}

可以知道关键的地方是能过context.getDrawable来拿到的,如果我们能重写这个函数是不是就可以搞定了?答案是不能,因为这个函数是final不能被重写的,实现如下:

public final Drawable getDrawable(@DrawableRes int id) {

return getResources().getDrawable(id, getTheme());

}

能怎么办呢,答案就从上面getDrawable函数的实现找到,getDrawable是能过getResources().getDrawable来实现的,如果能重写getResources,在这里判断是否是繁体,如果是则去zh目录下查找资源,来看getResources的实现:

// Context.java

public abstract Resources getResources();

看到这里,你应该知道这个思路是可以的!于是按思中路实现,首先判断是否是繁体,然后去默认中文目录下查找

第一步:

判断是用什么语言和国家可以用resources获取config

Locale locale = context.getResources().getConfiguration().locale;

if (locale.getLanguage().equals("zh") && !locale.getCountry().equals("CN")) {

// 繁体中文

}

第二步:

去中文目录查找,首先得有一个中文的resources,当前activity的resources是繁体的,是不能直接调用的,实现方法如下:

private static void updateResource(Resources resource, Locale l) {

Configuration config = resource.getConfiguration();

config.locale = l;

resource.updateConfiguration(config, null);

}

private static Resources getApplicationResource(PackageManager pm, String pkgName, Locale l) {

Resources resourceForApplication = null;

try {

resourceForApplication = pm.getResourcesForApplication(pkgName);

updateResource(resourceForApplication, l);

} catch (PackageManager.NameNotFoundException e) {

}

return resourceForApplication;

}

//得到简体中文的resources,由于简体中文没有资源,所以会去默认的中文下去找

Resources resources = getApplicationResource(context.getApplicationContext().getPackageManager(),

context.getPackageName(), new Locale("zh", "CN"));

综合起来是重写Activity的getResources:

@Override

public Resources getResources() {

Locale locale = context.getResources().getConfiguration().locale;

if (locale.getLanguage().equals("zh") && !locale.getCountry().equals("CN")) {

// 繁体中文

Resources resources = getApplicationResource(context.getApplicationContext().getPackageManager(),

context.getPackageName(), new Locale("zh", "CN"));

if (resources != null) {

return resources;

}

}

return super.getResources();

}

这样就实现了所有中文上都共用一套资源!上面的示例在xml中指定资源或者在代码里getDrawable可以这么使用,如果是用第三方图片加载库怎么实现?这里展示一下ImageLoader的实现:

ImageLoader加载资源是默认是通过BaseImageLoader去加载的,加载时会调用BaseDownloader去获取资源,我们只要自定义Downloader就可以了:

public class NotePaperImageDownloader extends BaseImageDownloader {

// 繁体中文时用的resources

Resources mResources;

public NotePaperImageDownloader(Context context) {

super(context);

}

public NotePaperImageDownloader(Context context, int connectTimeout, int readTimeout) {

super(context, connectTimeout, readTimeout);

}

@Override

protected InputStream getStreamFromDrawable(String imageUri, Object extra) {

Locale locale = context.getResources().getConfiguration().locale;

if (locale.getLanguage().equals("zh") && !locale.getCountry().equals("CN")) {

// 繁体中文,使用简体中文的资源进行加载,否则在没有指定繁体资源时默认会去英文下找,会不会去drawable-zh-xx去找

if (mResources == null) {

Resources resources = getApplicationResource(context.getApplicationContext().getPackageManager(),

context.getPackageName(), new Locale("zh", "CN"));

if (resources == null) {

return super.getStreamFromDrawable(imageUri, extra);

}

mResources = resources;

}

String drawableIdString = Scheme.DRAWABLE.crop(imageUri);

int drawableId = Integer.parseInt(drawableIdString);

return mResources.openRawResource(drawableId);

} else {

return super.getStreamFromDrawable(imageUri, extra);

}

}

}

总结:

关于android资源的加载,只要熟悉资源加载框架context/resources/assetmanger几个关键的类就可以做很多事。