系统Settrings分析及如何添加新的设置项
来源:互联网 发布:金猪报喜源码下载 编辑:程序博客网 时间:2024/05/01 09:40
系统Settings到底是什么?从结构上来看其实就是一个app。
那么我们打开AndroidManifest.xml这个文件,可以很容易定位到程序入口类Settings.java中。
<activity android:name="Settings" android:label="@string/settings_label_launcher" android:taskAffinity="com.android.settings" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <action android:name="android.settings.SETTINGS" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
打开Settings.java,发现他继承的是PreferenceActivity,既然是一个activity,那么我们很容易定位到oncreate()方法中去了。
@Override protected void onCreate(Bundle savedInstanceState) { if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) { getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0)); } hasGPS = getPackageManager() .hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS); mAuthenticatorHelper = new AuthenticatorHelper(); mAuthenticatorHelper.updateAuthDescriptions(this); mAuthenticatorHelper.onAccountsUpdated(this, null); mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE, Context.MODE_PRIVATE); getMetaData(); mInLocalHeaderSwitch = true; super.onCreate(savedInstanceState); mInLocalHeaderSwitch = false; if (!onIsHidingHeaders() && onIsMultiPane()) { highlightHeader(mTopLevelHeaderId); // Force the title so that it doesn't get overridden by a direct launch of // a specific settings screen. setTitle(R.string.settings_label); } // Retrieve any saved state if (savedInstanceState != null) { mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER); mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER); } // If the current header was saved, switch to it if (savedInstanceState != null && mCurrentHeader != null) { //switchToHeaderLocal(mCurrentHeader); showBreadCrumbs(mCurrentHeader.title, null); } if (mParentHeader != null) { setParentTitle(mParentHeader.title, null, new OnClickListener() { @Override public void onClick(View v) { switchToParent(mParentHeader.fragment); } }); } // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs if (onIsMultiPane()) { getActionBar().setDisplayHomeAsUpEnabled(false); getActionBar().setHomeButtonEnabled(false); } }刚开始在oncreate()方法中居然没有找到布局文件。后来上网搜了一下才知道一般在activity中设置布局,用的是setContentView(),在PreferenceActivity中,是需要继承onBuildHeaders(List)这个方法,那么我们来看看这个方法是怎么加载布局的吧。
@Override public void onBuildHeaders(List<Header> headers) { if (!onIsHidingHeaders()) { loadHeadersFromResource(R.xml.settings_headers, headers); updateHeaderList(headers); } }这边用到了loadHeadersFromResource(R.xml.settings_headers, headers);方法来进行加载,那么我们再定位到R.xml.settings_headers文件下。
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2010 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.--><preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <!-- WIRELESS and NETWORKS --> <header android:id="@+id/wireless_section" android:title="@string/header_category_wireless_networks" /> <!-- Wifi --> <header android:id="@+id/wifi_settings" android:fragment="com.android.settings.wifi.WifiSettings" android:title="@string/wifi_settings_title" android:icon="@drawable/ic_settings_wireless" /> <!-- Bluetooth --> <header android:id="@+id/bluetooth_settings" android:fragment="com.android.settings.bluetooth.BluetoothSettings" android:title="@string/bluetooth_settings_title" android:icon="@drawable/ic_settings_bluetooth2" /> <!-- Ethernet --> <header android:id="@+id/ethernet_settings" android:fragment="com.android.settings.ethernet.EthernetSettings" android:title="@string/ethernet_settings_title" android:icon="@drawable/ic_launcher" /> <!-- Data Usage --> <header android:id="@+id/data_usage_settings" android:fragment="com.android.settings.DataUsageSummary" android:title="@string/data_usage_summary_title" android:icon="@drawable/ic_settings_data_usage" /> <!-- Operator hook --> <header android:fragment="com.android.settings.WirelessSettings" android:id="@+id/operator_settings"> <intent android:action="com.android.settings.OPERATOR_APPLICATION_SETTING" /> </header> <!-- Other wireless and network controls --> <header android:id="@+id/wireless_settings" android:title="@string/radio_controls_title" android:breadCrumbTitle="@string/wireless_networks_settings_title" android:fragment="com.android.settings.WirelessSettings" android:icon="@drawable/ic_settings_more" /> <!-- DEVICE --> <header android:id="@+id/device_section" android:title="@string/header_category_device" /> <!-- Mult sim cards setting <header android:id="@+id/dual_settings" android:icon="@drawable/ic_settings_cardsetting" android:title="@string/dual_mode_settings_title"> <intent android:action="android.intent.action.MAIN" android:targetPackage="com.android.settings" android:targetClass="com.android.settings.MultSimCardsSettingsEx" /> </header> --> <!-- Mult sim cards setting --> <header android:id="@+id/dual_settings" android:icon="@drawable/ic_settings_cardsetting" android:fragment="com.android.settings.SetIPFragment" android:title="@string/multi_sim_settings_title"/> <!-- Home --> <header android:id="@+id/home_settings" android:icon="@drawable/ic_settings_home" android:fragment="com.android.settings.HomeSettings" android:title="@string/home_settings" /> <!-- Themes --> <header android:id="@+id/themes_settings" android:icon="@drawable/ic_settings_themes" android:title="@string/themes_settings_title"> <intent android:action="android.intent.action.MAIN" android:targetPackage="com.tmobile.themechooser" android:targetClass="com.tmobile.themechooser.ThemeChooser" /> </header> <!-- Phone --> <header android:id="@+id/phone_settings" android:icon="@drawable/ic_settings_call_setting" android:title="@string/phone_settings_title"> <intent android:action="android.intent.action.MAIN" android:targetPackage="com.android.phone" android:targetClass="com.android.phone.CallFeaturesSettingEntry" /> </header> <!-- Sound --> <header android:id="@+id/sound_settings" android:icon="@drawable/ic_settings_sound" android:fragment="com.android.settings.SoundSettings" android:title="@string/sound_settings" /> <!-- Display --> <header android:id="@+id/display_settings" android:icon="@drawable/ic_settings_display" android:fragment="com.android.settings.DisplaySettings" android:title="@string/display_settings" /> <!-- Lock screen --> <header android:id="@+id/lock_screen_settings" android:fragment="com.android.settings.cyanogenmod.LockscreenInterface" android:title="@string/lock_screen_title" android:icon="@drawable/ic_settings_lockscreen" /> <!-- ProfileMgr --> <header android:id="@+id/profile_manager_settings" android:icon="@drawable/ic_settings_profile" android:title="@string/profile_manager_settings_title"> <intent android:action="android.intent.action.MAIN" android:targetClass="com.android.profiles.ProfileMgr" android:targetPackage="com.android.profiles" /> </header> <!-- Storage --> <header android:id="@+id/storage_settings" android:fragment="com.android.settings.deviceinfo.Memory" android:icon="@drawable/ic_settings_storage" android:title="@string/storage_settings" /> <!-- Battery --> <header android:id="@+id/battery_settings" android:fragment="com.android.settings.fuelgauge.PowerUsageSummary" android:icon="@drawable/ic_settings_battery" android:title="@string/power_usage_summary_title" /> <!-- Application Settings --> <header android:fragment="com.android.settings.ApplicationSettings" android:icon="@drawable/ic_settings_applications" android:title="@string/applications_settings" android:id="@+id/application_settings" /> <!-- Manage users --> <header android:fragment="com.android.settings.users.UserSettings" android:icon="@drawable/ic_settings_multiuser" android:title="@string/user_settings_title" android:id="@+id/user_settings" /> <!-- Manage NFC payment apps --> <header android:fragment="com.android.settings.nfc.PaymentSettings" android:icon="@drawable/ic_settings_nfc_payment" android:title="@string/nfc_payment_settings_title" android:id="@+id/nfc_payment_settings" /> <!-- Manufacturer hook --> <header android:fragment="com.android.settings.WirelessSettings" android:id="@+id/manufacturer_settings"> <intent android:action="com.android.settings.MANUFACTURER_APPLICATION_SETTING" /> </header> <!-- PERSONAL --> <header android:id="@+id/personal_section" android:title="@string/header_category_personal" /> <!-- Location --> <header android:fragment="com.android.settings.location.LocationSettings" android:icon="@drawable/ic_settings_location" android:title="@string/location_settings_title" android:id="@+id/location_settings" /> <!-- Security --> <header android:fragment="com.android.settings.SecuritySettings" android:icon="@drawable/ic_settings_security" android:title="@string/security_settings_title" android:id="@+id/security_settings" /> <!-- Language --> <header android:id="@+id/language_settings" android:fragment="com.android.settings.inputmethod.InputMethodAndLanguageSettings" android:icon="@drawable/ic_settings_language" android:title="@string/language_settings" /> <!-- Backup and reset --> <header android:fragment="com.android.settings.PrivacySettings" android:icon="@drawable/ic_settings_backup" android:title="@string/privacy_settings" android:id="@+id/privacy_settings" /> <!-- ACCOUNTS section --> <header android:id="@+id/account_settings" android:title="@string/account_settings" /> <header android:id="@+id/account_add" android:title="@string/add_account_label" android:icon="@drawable/ic_menu_add_dark"> <intent android:action="android.settings.ADD_ACCOUNT_SETTINGS"/> </header> <!-- SYSTEM --> <header android:id="@+id/system_section" android:title="@string/header_category_system" /> <!-- Date & Time --> <header android:id="@+id/date_time_settings" android:fragment="com.android.settings.DateTimeSettings" android:icon="@drawable/ic_settings_date_time" android:title="@string/date_and_time_settings_title" /> <!-- Accessibility feedback --> <header android:id="@+id/accessibility_settings" android:fragment="com.android.settings.accessibility.AccessibilitySettings" android:icon="@drawable/ic_settings_accessibility" android:title="@string/accessibility_settings" /> <!-- Print --> <header android:id="@+id/print_settings" android:fragment="com.android.settings.print.PrintSettingsFragment" android:icon="@*android:drawable/ic_print" android:title="@string/print_settings" /> <!-- Development --> <header android:id="@+id/development_settings" android:fragment="com.android.settings.DevelopmentSettings" android:icon="@drawable/ic_settings_development" android:title="@string/development_settings_title" /> <!-- About Device --> <header android:id="@+id/about_settings" android:fragment="com.android.settings.DeviceInfoSettings" android:icon="@drawable/ic_settings_about" android:title="@string/about_settings" /></preference-headers>每一个header对应一个activity显示出来的fragment返回的view。仔细看下header的组成。
android:id:fragment 布局id用以判断这个fragment是否显示,或者布局类型(后面再说)
android:fragment :fragment加载的类,这个就是我们点击header弹出的新fragment
android:icon : header显示的图标
android:title :header 显示的名称
那么我们照葫芦画瓢自己定义一个试试看吧。
<!-- Ethernet --> <header android:id="@+id/ethernet_settings" android:fragment="com.android.settings.ethernet.EthernetSettings" android:title="@string/ethernet_settings_title" android:icon="@drawable/ic_launcher" />
果然当我定义新的header后,settings界面上多出了新的header,点击这个header跳转到了我定义的fragment上去了,值得注意的是不要忘记了在
AndroidManifest.xml 添加新的activity。既然已经实现了这个需求,那么我们跳出来看看,为什么吧。
前面说到了oncreate方法,主要说下getMetaData()方法,
private void getMetaData() { try { ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); if (ai == null || ai.metaData == null) return; mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID); mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); // Check if it has a parent specified and create a Header object final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE); String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); if (parentFragmentClass != null) { mParentHeader = new Header(); mParentHeader.fragment = parentFragmentClass; if (parentHeaderTitleRes != 0) { mParentHeader.title = getResources().getString(parentHeaderTitleRes); } } } catch (NameNotFoundException nnfe) { // No recovery } }其实由这里可以看出
mParentHeader = new Header(); mParentHeader.fragment = parentFragmentClass; if (parentHeaderTitleRes != 0) { mParentHeader.title = getResources().getString(parentHeaderTitleRes);这个方法用于设置mParentHeader的Fragment以及title。至于如何加载icon和fragment点击,因为判断较多,请大家自己看源码了。
至于如何智能的加载和去除header,请大家看我写的另外一篇博客了(主要是由updateHeaderList(List<Header> target)这个方法控制)。
多项目共享一份代码,如何控制项目是否显示settings里的preferceItem
那么有些细心的小伙伴会发现,我们settings里面每个header布局并不一样啊,有的是由名称+横线组成,有的有switch开关,有的只有名称;
这些功能如果需要实现大家怎么做呢?我第一时间想到了普通activity里如果有listview的话,每个item不一样,我们怎么处理?adapter,它返回的是一个view,那么我们就可以根据不同的判断返回不同类型的view了。果然,settings里面也是定义了adapter这个类。实现的方法和普通的adapter没什么不同。
private static class HeaderAdapter extends ArrayAdapter<Header> { static final int HEADER_TYPE_CATEGORY = 0; static final int HEADER_TYPE_NORMAL = 1; static final int HEADER_TYPE_SWITCH = 2; static final int HEADER_TYPE_BUTTON = 3; private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1; private final WifiEnabler mWifiEnabler; private final BluetoothEnabler mBluetoothEnabler; private AuthenticatorHelper mAuthHelper; private DevicePolicyManager mDevicePolicyManager; private static class HeaderViewHolder { ImageView icon; TextView title; TextView summary; Switch switch_; ImageButton button_; View divider_; } private LayoutInflater mInflater; static int getHeaderType(Header header) { if (header.fragment == null && header.intent == null) { return HEADER_TYPE_CATEGORY; } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) { return HEADER_TYPE_SWITCH; } else if (header.id == R.id.security_settings) { return HEADER_TYPE_BUTTON; } else { return HEADER_TYPE_NORMAL; } } @Override public int getItemViewType(int position) { Header header = getItem(position); return getHeaderType(header); } @Override public boolean areAllItemsEnabled() { return false; // because of categories } @Override public boolean isEnabled(int position) { return getItemViewType(position) != HEADER_TYPE_CATEGORY; } @Override public int getViewTypeCount() { return HEADER_TYPE_COUNT; } @Override public boolean hasStableIds() { return true; } public HeaderAdapter(Context context, List<Header> objects, AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) { super(context, 0, objects); mAuthHelper = authenticatorHelper; mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); // Temp Switches provided as placeholder until the adapter replaces these with actual // Switches inflated from their layouts. Must be done before adapter is set in super mWifiEnabler = new WifiEnabler(context, new Switch(context)); mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context)); mDevicePolicyManager = dpm; } @Override public View getView(int position, View convertView, ViewGroup parent) { HeaderViewHolder holder; Header header = getItem(position); int headerType = getHeaderType(header); View view = null; if (convertView == null) { holder = new HeaderViewHolder(); switch (headerType) { case HEADER_TYPE_CATEGORY: view = new TextView(getContext(), null, android.R.attr.listSeparatorTextViewStyle); holder.title = (TextView) view; break; case HEADER_TYPE_SWITCH: view = mInflater.inflate(R.layout.preference_header_switch_item, parent, false); holder.icon = (ImageView) view.findViewById(R.id.icon); holder.title = (TextView) view.findViewById(com.android.internal.R.id.title); holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary); holder.switch_ = (Switch) view.findViewById(R.id.switchWidget); break; case HEADER_TYPE_BUTTON: view = mInflater.inflate(R.layout.preference_header_button_item, parent, false); holder.icon = (ImageView) view.findViewById(R.id.icon); holder.title = (TextView) view.findViewById(com.android.internal.R.id.title); holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary); holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget); holder.divider_ = view.findViewById(R.id.divider); break; case HEADER_TYPE_NORMAL: view = mInflater.inflate( R.layout.preference_header_item, parent, false); holder.icon = (ImageView) view.findViewById(R.id.icon); holder.title = (TextView) view.findViewById(com.android.internal.R.id.title); holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary); break; } view.setTag(holder); } else { view = convertView; holder = (HeaderViewHolder) view.getTag(); } // All view fields must be updated every time, because the view may be recycled switch (headerType) { case HEADER_TYPE_CATEGORY: holder.title.setText(header.getTitle(getContext().getResources())); break; case HEADER_TYPE_SWITCH: // Would need a different treatment if the main menu had more switches if (header.id == R.id.wifi_settings) { mWifiEnabler.setSwitch(holder.switch_); } else { mBluetoothEnabler.setSwitch(holder.switch_); } updateCommonHeaderView(header, holder); break; case HEADER_TYPE_BUTTON: if (header.id == R.id.security_settings) { boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); if (hasCert) { holder.button_.setVisibility(View.VISIBLE); holder.divider_.setVisibility(View.VISIBLE); boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null; if (isManaged) { holder.button_.setImageResource(R.drawable.ic_settings_about); } else { holder.button_.setImageResource( android.R.drawable.stat_notify_error); } holder.button_.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent( android.provider.Settings.ACTION_MONITORING_CERT_INFO); getContext().startActivity(intent); } }); } else { holder.button_.setVisibility(View.GONE); holder.divider_.setVisibility(View.GONE); } } updateCommonHeaderView(header, holder); break; case HEADER_TYPE_NORMAL: updateCommonHeaderView(header, holder); break; } return view; }那么本质来讲,我们可以把header当做一个listpreferce。由setListAdapter(ListAdapter adapter)来进行加载不同种类的view。
@Override public void setListAdapter(ListAdapter adapter) { if (adapter == null) { super.setListAdapter(null); } else { DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm)); } }
由updateHeaderList(List<Header> target)方法加载布局的时候,进行种类选择。
if (i < target.size() && target.get(i) == header) { // Hold on to the first header, when we need to reset to the top-level if (mFirstHeader == null && HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) { mFirstHeader = header; } mHeaderIndexMap.put(id, i); i++; }讲到这里,settings基本流程已经差不多讲完了。~~~而然,并没有~,博主发现了新的问题!~
如果我有一个完善的app代码。如何在settings里面添加一个借口呢,点击这个借口启动的却是另外一个app呢。
如果我们仔细分析settings_headers.xml,会发现有些布局是不一样的,比如
<!-- Themes --> <header android:id="@+id/themes_settings" android:icon="@drawable/ic_settings_themes" android:title="@string/themes_settings_title"> <intent android:action="android.intent.action.MAIN" android:targetPackage="com.tmobile.themechooser" android:targetClass="com.tmobile.themechooser.ThemeChooser" /> </header>
我们看到这个header并没有定义fragment,但是它却定义了intent。
android:action="android.intent.action.MAIN" 通知settings,这个header需要优先启动intent
android:targetPackage="com.tmobile.themechooser" 告诉系统,在什么地方这到这个包。
android:targetClass="com.tmobile.themechooser.ThemeChooser" 这个一定要注意不是你存放app源文件的地址,一定得是编译完后,class文件存放的地址,博主刚开始不理解,只编译了settings,结果点击就报错了,之后将我添加的app一起编译后,就成功了,这个不难,大家尝试下就可以实现了。
- 系统Settrings分析及如何添加新的设置项
- Android添加一项新的系统设置项
- 如何在添加新的系统菜单项CMenu
- 如何添加新的系统调用
- 如何在Linux系统中添加新的系统调用
- Android: 如何向系统设置中添加自己的配置选项及配置页面
- 如何在Linux中添加新的系统调用
- 如何在Linux中添加新的系统调用
- 如何在Linux中添加新的系统调用
- 如何在Linux中添加新的系统调用
- 如何为Android系统添加一个新的资源包
- 如何给ecshop系统添加新的配送插件
- 如何添加新的字库
- 【转载】如何在Linux系统中添加新的系统调用
- Magento目录页设置布局及添加新布局的方法
- XCode5添加新VIEW编译找不到及控件不显示的设置
- 如何在内核中添加新的配置项
- linux 中如何查询到系统新添加的磁盘设备
- Emacs学习笔记(2)——minGW安装、Emacs调用minGW及error: CreateProcess: No such file or directory
- iOS学习--老菜鸟的storyboard学习之路
- 002.Plus One
- 深入理解js构造函数
- AFNetworking 3.0迁移指南
- 系统Settrings分析及如何添加新的设置项
- 一句话设计模式
- Android签名机制介绍:生成keystore、签名、查看签名信息等方法
- mysql Error Code
- UIGestureRegnizer 的一些注意点
- ubuntu下安装opencv 读取摄像头
- 2016年3月22日 14:48:59
- 类才有设计模式?用方法不行么?
- Android-->UDP协议的Socket数据传输