系统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);
这个方法用于设置mParentHeaderFragment以及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一起编译后,就成功了,这个不难,大家尝试下就可以实现了。





















0 0
原创粉丝点击