2.本文主要是对DisplaySettings进行分析
本章主要分析点击左图显示,显示中图,点击互动屏保后跳转到右图的过程。
在setting中的选项为都在R.xml.dashboard_categories中,点击后会加载fragment,本次研究的DisplaySettings如下:
可知点击后会调用com.android.settings.DisplaySettings这个fragment。但是点击处理的代码在哪里呢,一般情况下可以从SettingsActivity、DashboardSummary等代码中找,但是本次点击事件的响应是在DashboardTileView类的onclick函数中。
@Override
public void onClick(View v) {
if (mTile.fragment != null) {
Utils.startWithFragment(getContext(), mTile.fragment, mTile.fragmentArguments, null, 0,
mTile.titleRes, mTile.getTitle(getResources()));
} else if (mTile.intent != null) {
getContext().startActivity(mTile.intent);
}
}
很明显,mTile.fragment是"com.android.settings.DisplaySettings",不是null。进入Utils.startWithFragment()函数。
public static void startWithFragment(Context context, String fragmentName, Bundle args,
Fragment resultTo, int resultRequestCode, int titleResId,
CharSequence title) {
startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
null /* titleResPackageName */, titleResId, title, false /* not a shortcut */);
}
跳转一下
public static void startWithFragment(Context context, String fragmentName, Bundle args,
Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
CharSequence title, boolean isShortcut) {
Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
titleResId, title, isShortcut);
if (resultTo == null) {
context.startActivity(intent);
} else {
resultTo.startActivityForResult(intent, resultRequestCode);
}
}
先看onBuildStartFragmentIntent函数
public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
Bundle args, String titleResPackageName, int titleResId, CharSequence title,
boolean isShortcut) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClass(context, SubSettings.class);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
titleResPackageName);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
return intent;
}
就是返回一个intent,可以看出该intent其实是一个操作,启动一个SettingsActivity,并且把一个fragmentName放在参数中。然后就startActivity(intent);启动SettingsActivity。
继续进入SettingsActivity的onCreate函数,本次是将主要的函数列出:
getMetaData();
final Intent intent = getIntent();//获取intent
final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);//获取fragment名字
final ComponentName cn = intent.getComponent();
final String className = cn.getClassName();
mIsShowingDashboard = className.equals(Settings.class.getName());//本次的class是SettingsActivity,所以是false
setContentView(mIsShowingDashboard ?
R.layout.settings_main_dashboard : R.layout.settings_main_prefs);//本次加载的是R.layout.settings_main_prefs
setTitleFromIntent(intent);
Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
switchToFragment(initialFragmentName, initialArguments, true, false,mInitialTitleResId, mInitialTitle, false);//重点
首先看settings_main_prefs
其中有一个id为main_content的FrameLayout,容易猜出这个framelayout就是放fragment的。 重点分析switchToFragment
private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
if (validate && !isValidFragment(fragmentName)) {
throw new IllegalArgumentException("Invalid fragment for this activity: "
+ fragmentName);
}
Fragment f = Fragment.instantiate(this, fragmentName, args);//通过静态方法实例化一个fragment
FragmentTransaction transaction = getFragmentManager().beginTransaction();//获取fragment操作集合
transaction.replace(R.id.main_content, f);//将原容器中的内容替换为f
if (withTransition) {
TransitionManager.beginDelayedTransition(mContent);
}
if (addToBackStack) {
//在commit()方法之前,你可以调用addToBackStack(),把这个transaction加入back stack中去,这样按返回键时会出现被替换的fragment
transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
}
if (titleResId > 0) {
transaction.setBreadCrumbTitle(titleResId);
} else if (title != null) {
transaction.setBreadCrumbTitle(title);
}
transaction.commitAllowingStateLoss();
getFragmentManager().executePendingTransactions();//调用commit()方法并不能立即执行transaction中包含的改变动作,commit()方法把transaction加入activity的UI线程队列中。
// 但是,如果觉得有必要的话,可以调用executePendingTransactions()方法来立即执行commit()提供的transaction。
return f;
}
和上一篇一样的,这样就加载了一个fragment,至此就可以得到中图所示的效果。
点击中图所示的组件就可以跳转到具体的设置界面,下面进入该fragment研究其实现原理。
进入oncreate函数:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
..........
addPreferencesFromResource(R.xml.display_settings);//加载xml文件
mScreenSaverPreference = findPreference(KEY_SCREEN_SAVER);//获取Preference组件
mScreenTimeoutPreference = (ListPreference) findPreference(KEY_SCREEN_TIMEOUT);//获取ListPreference组件
final long currentTimeout = Settings.System.getLong(resolver, SCREEN_OFF_TIMEOUT,
FALLBACK_SCREEN_TIMEOUT_VALUE);
mScreenTimeoutPreference.setValue(String.valueOf(currentTimeout));
mScreenTimeoutPreference.setOnPreferenceChangeListener(this);
disableUnusableTimeouts(mScreenTimeoutPreference);
updateTimeoutPreferenceDescription(currentTimeout);
mFontSizePref = (WarnedListPreference) findPreference(KEY_FONT_SIZE);
mFontSizePref.setOnPreferenceChangeListener(this);
mFontSizePref.setOnPreferenceClickListener(this);
if (isAutomaticBrightnessAvailable(getResources())) {
mAutoBrightnessPreference = (SwitchPreference) findPreference(KEY_AUTO_BRIGHTNESS);
mAutoBrightnessPreference.setOnPreferenceChangeListener(this);
} else {
removePreference(KEY_AUTO_BRIGHTNESS);
} ........
}
先看加载的布局文件,再看获取的组件
.................
结合中图可以看出每一个PreferenceScreen、ListPreference或者SwitchPreference都是一个设置项在oncreate函数中也取出了每个组件,然后进行设置,设置过程比较简单。关键是确定点击的响应函数按常理还是从三个点入手,activity、fragment和Preference。
先看activity
implements PreferenceManager.OnPreferenceTreeClickListener,
PreferenceFragment.OnPreferenceStartFragmentCallback,
ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
SearchView.OnQueryTextListener, SearchView.OnCloseListener,
MenuItem.OnActionExpandListener
继承了很多,关键看点击之类的事件,在这看不出来。
看fragment:
public class DisplaySettings extends SettingsPreferenceFragment implements
Preference.OnPreferenceChangeListener, OnPreferenceClickListener, Indexable
继承了SettingsPreferenceFragment
public class SettingsPreferenceFragment extends PreferenceFragment implements DialogCreatable
看PreferenceFragment
/**
* Interface that PreferenceFragment's containing activity should
* implement to be able to process preference items that wish to
* switch to a new fragment.
*/
public interface OnPreferenceStartFragmentCallback {
/**
* Called when the user has clicked on a Preference that has
* a fragment class name associated with it. The implementation
* to should instantiate and switch to an instance of the given
* fragment.
*/
boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref);
}
英文也比较容易理解,在PreferenceFragment中明确说明 这个接口必须被包含PreferenceFragment的activity实现,才能点击preference items时跳转到新的fragment。
public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
// Override the fragment title for Wallpaper settings
int titleRes = pref.getTitleRes();
if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
titleRes = R.string.wallpaper_settings_fragment_title;
} else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
&& UserHandle.myUserId() != UserHandle.USER_OWNER) {
if (UserManager.get(this).isLinkedUser()) {
titleRes = R.string.profile_info_settings_title;
} else {
titleRes = R.string.user_info_settings_title;
}
}
startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
null, 0);
return true;
}
public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
CharSequence titleText, Fragment resultTo, int resultRequestCode) {
String title = null;
if (titleRes < 0) {
if (titleText != null) {
title = titleText.toString();
} else {
// There not much we can do in that case
title = "";
}
}
Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
titleRes, title, mIsShortcut);
}
至此图中的三个过程分析结束。