Android原生Settings源码分析

最近要修改Settings相关的东西,就顺便研究下Setings的代码结构,特做个记录

 

由于修改的是Android4.4的平台,所以以下都是在Android4.4基础上分析的。

 

1、入口

从packages/apps/settings/AndroidManifestxml找到Settings模块的入口:

<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的第一个Activity为Settings.java

 

2、Settings extends PreferenceActivity

settings继承与preverenceActivity,所以我们要主要两个重载函数onBuildHeaders和setListAdapter

1)onBuildHeaders

onbuildHeaders负责加载xml布局文件,这里的xml文件settings_headers使用的是Preference Headers,没有使用传统的PreferenceScreen。

每个Header对应一个子界面,一般为Fragment

关于这点可以参考http://www.xuebuyuan.com/549370.html

@Override
    public void onBuildHeaders(List<Header> headers) {
        if (!onIsHidingHeaders()) {
            loadHeadersFromResource(R.xml.settings_headers, headers);
            updateHeaderList(headers);
        }
    }

 

2)setListAdapter

PreferenceActivity继承于ListActivity,所以这里可以使用需要ListView创建Adapter,这里的自定义Adapter对不同子菜单的分别处理有关键性作用

 

@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));        }    }

 

自定义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.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;
        }

3、settings设置数据的保存

虽然preference是可以记录状态,并保存在sharedpreference文件中,但是有很多状态值是全局的,比如wifi,蓝牙等。所以这里涉及到全局数据的保存

以蓝牙为例,蓝牙打开关闭的操作在BluetoothEnabler.java里面

当按下蓝牙开关时,调用mLocalAdapter.setBluetoothEnabled(isChecked);–>BluetoothAdapter.enable()–>BluetoothAdapter:mManagerService.enable();–>BluetoothManagerService:enable–>BluetoothManagerService:persistBluetoothSetting

 private void persistBluetoothSetting(int value) {
        Settings.Global.putInt(mContext.getContentResolver(),
                               Settings.Global.BLUETOOTH_ON,
                               value);
    }

最终改的是Settings.Global.BLUETOOTH_ON的值,而这个默认值则在DatabaseHelper.java中设置

loadBooleanSetting(stmt, Settings.Global.BLUETOOTH_ON,
                    R.bool.def_bluetooth_on);

其他的值,比如wifi开关,默认语言等,都类似于这个过程

 

 

 

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/wangwei6227/article/details/65444885
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞