FrameWork开发之路首先得玩得转系统APP,个人是在Android5.0的基础上进行定制,Settings模块分为两个部分:
packages\app\Settings 下的APP代码部分
frameworks/base/packages/SettingsProvider/ 数据库
frameworks/base/core/java/android/provider/Settings.java
首先从APP代码部分入手,UI界面架构是有3部分组成实现可配置可扩展,先理解PreferenceFragment, PreferenceScreen,PreferenceCategory, Preference的关系,介绍的最详细的就是谷歌官方文档了,参照链接
我这边首先将的是如何自定义PreferenceCategory和自定义Preference,首先来看系统如何来操作的,参考设置存储部分的界面
packages\apps\Settings\src\com\android\settings\deviceinfo\Memory.java就是fragment界面
###fragment核心代码块
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Context context = getActivity();
'''preferenceFragment布局文件'''
addPreferencesFromResource(R.xml.device_info_memory);
for (StorageVolume volume : storageVolumes) {
if (mMemoryExts.isAddPhysicalCategory(volume)) {
addCategory(StorageVolumePreferenceCategory.buildForPhysical(context, volume));
}
}
}
private void addCategory(StorageVolumePreferenceCategory category) {
mCategories.add(category);
'''add多个category模块'''
getPreferenceScreen().addPreference(category);
category.init();
}
对应的XMl,device_info_xml文件内容:
`
可以看出默认存储器,遥控存储器,总容量都是作为preference合并到一个category,然后add到preferenceScreen里面,下面来看PreferenceCategory怎么自定义,packages\apps\Settings\src\com\android\settings\deviceinfo下的StorageVolumePreferenceCategory.java
public class StorageVolumePreferenceCategory extends PreferenceCategory {
private StorageVolumePreferenceCategory(Context context, StorageVolume volume) {
super(context);
mVolume = volume;
mMeasure = StorageMeasurement.getInstance(context, volume);
mResources = context.getResources();
mStorageManager = StorageManager.from(context);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mCategoryExts = new StorageVolumePreferenceCategoryExts(context, mVolume);
mCategoryExts.setVolumeTitle(this);
}
private StorageItemPreference buildItem(int titleRes, int colorRes) {
return new StorageItemPreference(getContext(), titleRes, colorRes);
}
public void init() {
final Context context = getContext();
'''遥控器存储作为自定义的Preference'''
mUsageBarPreference = new UsageBarPreference(context);
mUsageBarPreference.setOrder(ORDER_USAGE_BAR);
addPreference(mUsageBarPreference);
'''总容量作为自定义的Preference'''
mItemTotal = buildItem(R.string.memory_size, 0);
mItemAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
addPreference(mItemTotal);
addPreference(mItemAvailable);
'''各个类别文件存储作为自定义的Preference'''
mItemApps = buildItem(R.string.memory_apps_usage, R.color.memory_apps_usage);
mItemDcim = buildItem(R.string.memory_dcim_usage, R.color.memory_dcim);
mItemMusic = buildItem(R.string.memory_music_usage, R.color.memory_music);
mItemDownloads = buildItem(R.string.memory_downloads_usage, R.color.memory_downloads);
mItemCache = buildItem(R.string.memory_media_cache_usage, R.color.memory_cache);
mItemMisc = buildItem(R.string.memory_media_misc_usage, R.color.memory_misc);
mItemCache.setKey(KEY_CACHE);
/** M: CR ALPS01309473, Set storageItem keys.@{*/
mItemApps.setKey(KEY_APPS);
mItemDcim.setKey(KEY_DCIM);
mItemMusic.setKey(KEY_MUSIC);
mItemDownloads.setKey(KEY_DOWNLOADS);
mItemMisc.setKey(KEY_MISC);
/** @} */
final boolean showDetails = mVolume == null || mVolume.isPrimary();
if (showDetails) {
if (showUsers) {
addPreference(new PreferenceHeader(context, currentUser.name));
}
'''Preference 添加到Category的过程'''
addPreference(mItemApps);
addPreference(mItemDcim);
addPreference(mItemMusic);
addPreference(mItemDownloads);
addPreference(mItemCache);
addPreference(mItemMisc);
}
}
}
下面看如何自定义各种UI效果的Preference,本质就是自定义View披上一层外套,从简单的入手,音量设置的preference个人认为好理解
\packages\apps\Settings\src\com\android\audioprofile\AudioProfilePreference.java
public class AudioProfilePreference extends Preference {
public AudioProfilePreference(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
mContext = context;
mInflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// get the title from audioprofile_settings.xml
'''Preference属性Title的设置'''
if (super.getTitle() != null) {
mPreferenceTitle = super.getTitle().toString();
}
// get the summary from audioprofile_settings.xml
'''Preference属性Summary副标题的设置'''
if (super.getSummary() != null) {
mPreferenceSummary = super.getSummary().toString();
}
mProfileManager = (AudioProfileManager) context
.getSystemService(Context.AUDIO_PROFILE_SERVICE);
'''Preference属性Key本质就是SharedPreference种的Key设置'''
mKey = getKey();
mExt = UtilsExt.getAudioProfilePlgin(context);
}
'''PreferenceUI效果'''
@Override
public View onCreateView(ViewGroup parent) {
Xlog.d(XLOGTAG, TAG + "onCreateView " + getKey());
View view = mExt.createView(R.layout.audio_profile_item);
mCheckboxButton = (RadioButton) mExt
.getPrefRadioButton(R.id.radiobutton);
mTextView = (TextView) mExt.getPreferenceTitle(R.id.profiles_text);
mSummary = (TextView) mExt.getPreferenceSummary(R.id.profiles_summary);
}
}
看一下Preference的UI xml文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
>
<LinearLayout android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"/>
<RadioButton
android:id="@+id/radiobutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:focusable="false"/>
/>
哈哈,本质就是自定义View了
还有一个小的知识点:官方文档介绍PreferenceScreen跳转Activity的时候没有介绍到Intent怎么给Activity传值,所以我就贴上这个小的知识点
<PreferenceScreen android:key="toggle_storage_settings"
android:title="@string/storage_settings">
<intent android:action="settings.advanced.fragment"
android:targetPackage="com.android.settings">
<extra android:name="Zhongqi.Shao" android:value="com.android.settings.deviceinfo.Memory"/>
intent>
PreferenceScreen>
到这应该理解Settings UI效果中的每一个元素关系,现在可以看Settings布局UI效果的实现原理
packages\apps\Settings\src\com\android\settings\SettingsActivity.java
启动的Activity是继承SettingsActivity的Settings.java,参见SettingsActivity.onCreate()函数本质就是Framelayout切换Fragment的效
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
'''就是空空的FrameLayout'''
setContentView(mIsShowingDashboard ?
R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
'''中间代码省略'''
switchToFragment(DashboardSummary.class.getName(), null, false, false,
mInitialTitleResId, mInitialTitle, false);
}
下面来看packages\apps\Settings\src\com\android\settings\dashboardDashboardSummary.java的代码
private void rebuildUI(Context context) {
long start = System.currentTimeMillis();
final Resources res = getResources();
mDashboard.removeAllViews();
'''又回到SettingsActivity.getDashboardCategories()'''
List categories =
((SettingsActivity) context).getDashboardCategories(true);
}
'''SettingsActivity中getDashboardCategories()实现'''
public List getDashboardCategories(boolean forceRefresh) {
if (forceRefresh || mCategories.size() == 0) {
buildDashboardCategories(mCategories);
}
return mCategories;
}
private void buildDashboardCategories(List categories) {
categories.clear();
'''重点 通过解析xml来实现配置加载fragment'''
loadCategoriesFromResource(R.xml.dashboard_categories, categories);
updateTilesList(categories);
}
<dashboard-categories
xmlns:android="http://schemas.android.com/apk/res/android">
<dashboard-category
android:id="@+id/wireless_section"
android:title="@string/header_category_wireless_networks" >
<dashboard-tile
android:id="@+id/wifi_settings"
android:title="@string/wifi_settings_title"
android:fragment="com.android.settings.wifi.WifiSettings"
android:icon="@drawable/ic_settings_wireless"
/>
<dashboard-tile
android:id="@+id/hetcomm_settings"
android:icon="@drawable/ic_settings_hetcomm"
android:title="@string/hetcom_setting_title">
<intent android:action="com.android.settings.HETCOMM_SETTINGS" />
dashboard-tile>
<dashboard-tile
android:id="@+id/bluetooth_settings"
android:title="@string/bluetooth_settings_title"
android:fragment="com.android.settings.bluetooth.BluetoothSettings"
android:icon="@drawable/ic_settings_bluetooth2"
/>
<dashboard-tile
android:id="@+id/hotknot_settings"
android:title="@string/hotknot_settings_title"
android:fragment="com.mediatek.settings.hotknot.HotKnotSettings"
android:icon="@drawable/ic_settings_hotknot"
/>
<dashboard-tile
android:id="@+id/sim_settings"
android:title="@string/sim_settings_title"
android:fragment="com.android.settings.sim.SimSettings"
android:icon="@drawable/ic_sim_sd"
/>
<dashboard-tile
android:id="@+id/data_usage_settings"
android:title="@string/data_usage_summary_title"
android:fragment="com.android.settings.DataUsageSummary"
android:icon="@drawable/ic_settings_data_usage"
/>
dashboard-category>
<dashboard-category
android:id="@+id/device_section"
android:title="@string/header_category_device" >
<dashboard-tile
android:id="@+id/home_settings"
android:title="@string/home_settings"
android:fragment="com.android.settings.HomeSettings"
android:icon="@drawable/ic_settings_home"
/>
<dashboard-tile
android:id="@+id/display_settings"
android:title="@string/display_settings"
android:fragment="com.android.settings.DisplaySettings"
android:icon="@drawable/ic_settings_display"
/>
<dashboard-tile
android:id="@+id/notification_settings"
android:title="@string/notification_settings"
android:fragment="com.mediatek.audioprofile.AudioProfileSettings"
android:icon="@drawable/ic_settings_notifications"
/>
<dashboard-tile
android:id="@+id/battery_settings"
android:title="@string/power_usage_summary_title"
android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"
android:icon="@drawable/ic_settings_battery"
/>
<dashboard-tile
android:id="@+id/application_settings"
android:title="@string/applications_settings"
android:fragment="com.android.settings.applications.ManageApplications"
android:icon="@drawable/ic_settings_applications"
/>
<dashboard-tile
android:id="@+id/user_settings"
android:title="@string/user_settings_title"
android:fragment="com.android.settings.users.UserSettings"
android:icon="@drawable/ic_settings_multiuser"
/>
<dashboard-tile
android:id="@+id/nfc_payment_settings"
android:title="@string/nfc_payment_settings_title"
android:fragment="com.android.settings.nfc.PaymentSettings"
android:icon="@drawable/ic_settings_nfc_payment"
/>
dashboard-category>
<dashboard-category
android:id="@+id/personal_section"
android:title="@string/header_category_personal" >
<dashboard-tile
android:id="@+id/location_settings"
android:title="@string/location_settings_title"
android:fragment="com.android.settings.location.LocationSettings"
android:icon="@drawable/ic_settings_location"
/>
<dashboard-tile
android:id="@+id/security_settings"
android:title="@string/security_settings_title"
android:fragment="com.android.settings.SecuritySettings"
android:icon="@drawable/ic_settings_security"
/>
<dashboard-tile
android:id="@+id/account_settings"
android:title="@string/account_settings_title"
android:fragment="com.android.settings.accounts.AccountSettings"
android:icon="@drawable/ic_settings_accounts"
/>
<dashboard-tile
android:id="@+id/language_settings"
android:title="@string/language_settings"
android:fragment="com.android.settings.inputmethod.InputMethodAndLanguageSettings"
android:icon="@drawable/ic_settings_language"
/>
<dashboard-tile
android:id="@+id/privacy_settings"
android:title="@string/privacy_settings"
android:fragment="com.android.settings.PrivacySettings"
android:icon="@drawable/ic_settings_backup"
/>
dashboard-category>
dashboard-categories>
到这基本UI可以说是过来一遍,下面会来记录我这边是怎么来深度定制