Android 4.4 Settings 应用分析

来源:互联网 发布:hp1505n网络打印设置 编辑:程序博客网 时间:2024/05/22 03:50

http://www.tuicool.com/articles/aQ7RRnR

一次偶然要在设置里面增加一个菜单,需要修改到settings_headers.xml 文件(res/layout/xml) 文件,所以就觉得要看一下这个流程.就做一下笔记,语言组织能力不行啊.

分析Android 源码的时候导入单个应用的时候一般是会有很多错误的,因为需要导入系统编译之后生成的jar包才能消除eclipse 里面的哪些红色xx.

1.Settings的UI

2.流程分析

从AndroidManifest.xml 中查看

<category android:name="android.intent.category.LAUNCHER" /> 知道Settings.java 是这个应用入口activity.

Settings 继承了PreferenceActivity .他的布局文件是settings_headers.xml

这个文件里面都是这些header,效果可以参考上面的效果图1和图2.

<!-- WIRELESS and NETWORKS  分类-->  <header android:id="@+id/wireless_section"    android:title="@string/header_category_wireless_networks" />  <!-- Sim management 普通项-->  <header    android:id="@+id/sim_settings"    android:icon="@drawable/ic_settings_dualsim"    android:fragment="com.mediatek.gemini.SimManagement"    android:title="@string/gemini_sim_management_title" />

com.android.settings.Settings.java 这个activity 是通过回调onBuildHeaders方法来加载进入应用之后的第一个布局文件的,然后调用 loadHeadersFromResource(R.xml.settings_headers, headers);

来解析 文件.

onBuildHeaders 和loadHeadersFromResource 方法都是父类PreferenceActivity 的方法.

Settings.java 重写onBuildHeaders 方法的实现的源码如下:

/**   * Populate the activity with the top-level headers.   */  @Override  public void onBuildHeaders(List<Header> headers) {    if (!onIsHidingHeaders()) {      PDebug.Start("loadHeadersFromResource");      loadHeadersFromResource(R.xml.settings_headers, headers);      PDebug.End("loadHeadersFromResource");      updateHeaderList(headers);    }  }

loadHeadersFromResource 方法就是解析settings_headers.xml 文件并保持相关的数据到List<Header> headers 里面.

Header 定义很多变量来和settings_headers.xml 里面节点一一对应,

public long id = HEADER_ID_UNDEFINED;

public int titleRes;

public CharSequence title;

public String fragment;

public Bundle fragmentArguments;

public Intent intent;

public Bundle extras;

………

通过跟踪Setting.java 的父类(PreferenceActivity)的继承关系知道他其实也是一个ListActivity.java ,全部的设置项也是使用ListView来显示的.

HeaderAdapter这个适配类是Setting.java 的内部类,它会判断之后来加载对应的view和数据来显示UI.

HeaderAdapter已经定义了4中类型的View 类型

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;//按钮项

前3种应该都见过,为了让大家看到第4项,我把稍微修改了一下我的HeaderAdapter源码(见getview方法的中的有//add的部分),也就是上面图2中的security 选项.

HeaderAdapter 的getHeaderType 方法决定了配置在settings_headers.xml 里面的header的类型.

HeaderAdapter 的getView 方法根据header的类型 来加载对应的布局文件.

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           || header.id == R.id.hotknot_settings) {        return HEADER_TYPE_SWITCH;      } else if (header.id == R.id.security_settings) {        return HEADER_TYPE_BUTTON;      } else {        return HEADER_TYPE_NORMAL;      }    }

但要注意的是在getView方法里面,当发现一个header 的类型是button的时候也会给header 的button增加一个onclick事件的,这个事件和header本事的onHeaderClick 是没有冲突的,因为2者不受同一个控件.

@Override    public View getView(int position, View convertView, ViewGroup parent) {      HeaderViewHolder holder;      Header header = getItem(position);      int headerType = getHeaderType(header);      Log.d("zhangle","getHeaderType" + header.title  + " headerType=" + headerType);      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 if (header.id == R.id.bluetooth_settings){            mBluetoothEnabler.setSwitch(holder.switch_);          } else if (header.id == R.id.hotknot_settings){            mHotKnotEnabler.setSwitch(holder.switch_);          }          updateCommonHeaderView(header, holder);          break;        case HEADER_TYPE_BUTTON:          if (header.id == R.id.security_settings) {            boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();            hasCert = true;//add            if (hasCert) {              holder.button_.setVisibility(View.VISIBLE);              holder.divider_.setVisibility(View.VISIBLE);              boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;              isManaged = true; //add              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;      }      // /M: add for sim management feature      if (header.id == R.id.sim_settings) {        /// M: Customize SIM string        holder.title.setText(mExt.customizeSimDisplayString(          getContext().getString(R.string.gemini_sim_management_title), SLOT_ALL));        handleDisableHolder(holder, view);      } else {        handleEnableHolder(holder, view);      }      return view;    }

那么每一个header 是如果响应点击操作的呢.这个就要看Setting.java的onHeaderClick 方法了, onHeaderClick 方法会调用父类的onHeaderClick方法来打开相关的应用,其父类是根据我们配置在settings_headers.xml里面的fragment和intent 来打开相对应的activity的.

Setting.java -- onHeaderClick

public void onHeaderClick(Header header, int position) {  boolean revert = false;  if (header.id == R.id.account_add) {      revert = true;  }  super.onHeaderClick(header, position);  if (revert && mLastHeader != null) {      highlightHeader((int) mLastHeader.id);  } else {      mLastHeader = header;  }    }

PreferenceActivity -- onHeaderClick

public void onHeaderClick(Header header, int position) {    if (header.fragment != null) {      if (mSinglePane) {        Log.d(TAG, "onHeaderClick, single pane and startWithFragment.");        int titleRes = header.breadCrumbTitleRes;        int shortTitleRes = header.breadCrumbShortTitleRes;        if (titleRes == 0) {          titleRes = header.titleRes;          shortTitleRes = 0;        }        startWithFragment(header.fragment, header.fragmentArguments, null, 0,            titleRes, shortTitleRes);      } else {        Log.d(TAG, "onHeaderClick, multiple pane and switchToHeader.");        switchToHeader(header);      }    } else if (header.intent != null) {      Log.d(TAG, "onHeaderClick, start activity with header intent.");      startActivity(header.intent);    }  }

0 0
原创粉丝点击