Android手机语言切换行为,是通过设置-语言和输入法-语言来改变手机的语言,其实这个功能很少被用户使用。
以Android5.1工程源码为基础,从设置app入手来分析和学习语言切换的过程:
一、语言设置界面:
首先在设置app中找到语言设置这个Preference,目前设置中界面大多都是Fragment,先找到语言和输入法的PreferenceScreen,与其对应的Fragment是InputMethodAndLanguageSettings.java,在其onCreate()方法中,首先是增加语言设置的preference:
addPreferencesFromResource(R.xml.language_settings);
找到language_settings.xml,可发现如下代码:
<PreferenceScreen
android:key="phone_language"
android:title="@string/phone_language"
android:fragment="com.android.settings.LocalePicker"
/>
于是断定LocalePicker就是语言设置的Fragment,它是ListFragment的子类,继承于framework中LocalePicker,并实现了父类的一个接口,其回调方法是onLocaleSelected(),Locale中文含义大致是语言环境,所以可推测这是设置语言后的一个回调方法,不确定的话,可打断点测试一下。然而此类中并没有关于语言设置界面数据适配的太多逻辑,
只是通过父类的方法创建了一个view:
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = super.onCreateView(inflater, container, savedInstanceState);
final ListView list = (ListView) view.findViewById(android.R.id.list);
Utils.forcePrepareCustomPreferencesList(container, view, list, false);
return view;
}
所以更多逻辑应该在framework中的LocalePicker.java中。既然是ListFragment,那就必须有Adapter,在此类中有构建了一个Adapter:
/**
* Constructs an Adapter object containing Locale information. Content is sorted by
* {@link LocaleInfo#label}.
*/
public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
return constructAdapter(context, R.layout.locale_picker_item, R.id.locale);
}
public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
final int layoutId, final int fieldId) {
boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
//获取系统支持语言的信息
final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode);
final LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
TextView text;
if (convertView == null) {
view = inflater.inflate(layoutId, parent, false);
text = (TextView) view.findViewById(fieldId);
view.setTag(text);
} else {
view = convertView;
text = (TextView) view.getTag();
}
LocaleInfo item = getItem(position);
text.setText(item.toString());
text.setTextLocale(item.getLocale());
return view;
}
};
}
而此方法通过getAllAssetLocales()方法获取系统支持语言的信息:
public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
final Resources resources = context.getResources();
//获取系统所支持的语言
final String[] locales = Resources.getSystem().getAssets().getLocales();
List<String> localeList = new ArrayList<String>(locales.length);
Collections.addAll(localeList, locales);
// Don't show the pseudolocales unless we're in developer mode.
if (!isInDeveloperMode) {
localeList.remove("ar-XB");
localeList.remove("en-XA");
}
Collections.sort(localeList);
final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size());
for (String locale : localeList) {
final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
if (l == null || "und".equals(l.getLanguage())
|| l.getLanguage().isEmpty() || l.getCountry().isEmpty()) {
continue;
}
if (localeInfos.isEmpty()) {
if (DEBUG) {
Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
}
localeInfos.add(new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l));
} else {
// check previous entry:
// same lang and a country -> upgrade to full name and
// insert ours with full name
// diff lang -> insert ours with lang-only name
final LocaleInfo previous = localeInfos.get(localeInfos.size() - 1);
if (previous.locale.getLanguage().equals(l.getLanguage()) &&
!previous.locale.getLanguage().equals("zz")) {
if (DEBUG) {
Log.v(TAG, "backing up and fixing " + previous.label + " to " +
getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames));
}
previous.label = toTitleCase(getDisplayName(
previous.locale, specialLocaleCodes, specialLocaleNames));
if (DEBUG) {
Log.v(TAG, " and adding "+ toTitleCase(