Android Settings開發之修改

原文鏈接:http://blog.csdn.net/wangjinyu501/article/details/22077803

下面是Seeings應用的截圖:

  可以看出這是很典型的使用了Fragment後的界面,設置裏面有WIFI、藍牙、顯示、存儲、應用等衆多功能。左邊的每一項,對應着右邊的一個設置界面,Fragment有四個子類:DialogFragment, ListFragment, PreferenceFragment, WebViewFragment。很明顯,Settings用的是PreferenceFragment。接着看一下Settings源碼package結構:

   主題部分的實現主要在com.android.settings下面,其他包主要是用於各自功能實現,所以重點說這個包下面的類。在AndroidManifest.xml文字中,看到程序入口是Settings類:

   打開Settings類,是繼承於PreferenceActivity:

   其他的繼承關係如下:PreferenceActivity --> ListActivity --> Activity。PreferenceActivity主要用於Settings,關於如何使用可以參考API(http://developer.android.com/reference/android/preference/PreferenceActivity.html)以及guide(http://developer.android.com/guide/topics/ui/settings.html)。和它相關聯的類有header、fragment、preference。每一個header就是左邊的一個選項條目,像藍牙、應用等,選擇之後右邊就會顯示對應的fragment(平板),然後fragment和preference聯繫在一起,組成了一個個設置項。一般在activity中設置佈局,用的是setContentView(),在PreferenceActivity中,是需要繼承onBuildHeaders(List)這個方法,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. @Override  
  2. ublic void onBuildHeaders(List<Header> target) {  
  3.   loadHeadersFromResource(R.xml.preference_headers, target);  
  4.    
  去生成選項表,點擊選項表的一個條目,右邊顯示對應的Fragment,這就是很典型的header+fragment組合,所以如果想在Settings基礎之上添加條目的話,在這個方法裏面的xml文件中添加即可,然後對應上fragment。下面分析一下執行流程:
  首先進入onCreate(Bundle savedInstanceState)方法裏面,代碼如下:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. @Override  
  2.     protected void onCreate(Bundle savedInstanceState) {  
  3.         if (getIntent().getBooleanExtra(EXTRA_CLEAR_UI_OPTIONS, false)) {  
  4.             getWindow().setUiOptions(0);  
  5.         }  
  6.         mAuthenticatorHelper = new AuthenticatorHelper();  
  7.         mAuthenticatorHelper.updateAuthDescriptions(this);  
  8.         mAuthenticatorHelper.onAccountsUpdated(this, null);  
  9.         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,  
  10.                 Context.MODE_PRIVATE);  
  11.         getMetaData();  
  12.         mInLocalHeaderSwitch = true;  
  13.         super.onCreate(savedInstanceState);  
  14.         mInLocalHeaderSwitch = false;  
  15.         if (!onIsHidingHeaders() && onIsMultiPane()) {  
  16.             highlightHeader(mTopLevelHeaderId);  
  17.             // Force the title so that it doesn't get overridden by a direct launch of  
  18.             // a specific settings screen.  
  19.             setTitle(R.string.settings_label);  
  20.         }  
  21.         // Retrieve any saved state  
  22.         if (savedInstanceState != null) {  
  23.             mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);  
  24.             mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);  
  25.         }  
  26.         // If the current header was saved, switch to it  
  27.         if (savedInstanceState != null && mCurrentHeader != null) {  
  28.             //switchToHeaderLocal(mCurrentHeader);  
  29.             showBreadCrumbs(mCurrentHeader.title, null);  
  30.         }  
  31.         if (mParentHeader != null) {  
  32.             setParentTitle(mParentHeader.title, null, new OnClickListener() {  
  33.                 public void onClick(View v) {  
  34.                     switchToParent(mParentHeader.fragment);  
  35.                 }  
  36.             });  
  37.         }  
  38.         // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs  
  39.         if (onIsMultiPane()) {  
  40.             getActionBar().setDisplayHomeAsUpEnabled(false);  
  41.             getActionBar().setHomeButtonEnabled(false);  
  42.         }  
  43.     }  
  第一個if用於設置window ui的對修改來說不用考慮了,意義不大,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. mAuthenticatorHelper = new AuthenticatorHelper();  
  2. mAuthenticatorHelper.updateAuthDescriptions(this);  
  3. mAuthenticatorHelper.onAccountsUpdated(this, null);  
  這個段代碼用於認證以及更新賬戶信息,接着往下看:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,Context.MODE_PRIVATE);  
  用於之後保存數據,然後是getMetaData();這個方法,代碼如下:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. private void getMetaData() {  
  2.         try {  
  3.             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),  
  4.                     PackageManager.GET_META_DATA);  
  5.             if (ai == null || ai.metaData == null) return;  
  6.             mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);  
  7.             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);  
  8.             // Check if it has a parent specified and create a Header object  
  9.             final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);  
  10.             String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);  
  11.             if (parentFragmentClass != null) {  
  12.                 mParentHeader = new Header();  
  13.                 mParentHeader.fragment = parentFragmentClass;  
  14.                 if (parentHeaderTitleRes != 0) {  
  15.                     mParentHeader.title = getResources().getString(parentHeaderTitleRes);  
  16.                 }  
  17.             }  
  18.         } catch (NameNotFoundException nnfe) {  
  19.             // No recovery  
  20.         }  
  21.     }  
  這個方法用於設置mParentHeader的Fragment以及title。下面具體舉兩個例子,關於如何修改Settings。
一、添加headers
      header即是左邊的菜單,如下圖左側。它的佈局文件在res下的xml文件夾中,名字是settings_headers.xml。打開如下:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!--  
  3.      Copyright (C) 2010 The Android Open Source Project  
  4.   
  5.      Licensed under the Apache License, Version 2.0 (the "License");  
  6.      you may not use this file except in compliance with the License.  
  7.      You may obtain a copy of the License at  
  8.   
  9.           http://www.apache.org/licenses/LICENSE-2.0  
  10.   
  11.      Unless required by applicable law or agreed to in writing, software  
  12.      distributed under the License is distributed on an "AS IS" BASIS,  
  13.      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  14.      See the License for the specific language governing permissions and  
  15.      limitations under the License.  
  16. -->  
  17.   
  18. <preference-headers xmlns:android="http://schemas.android.com/apk/res/android" >  
  19.   
  20.     <!-- WIRELESS and NETWORKS -->  
  21.     <header  
  22.         android:id="@+id/wireless_section"  
  23.         android:title="@string/header_category_wireless_networks" />  
  24.   
  25.     <!-- Wifi -->  
  26.     <header  
  27.         android:id="@+id/wifi_settings"  
  28.         android:fragment="com.android.settings.wifi.WifiSettings"  
  29.         android:icon="@drawable/ic_settings_wireless"  
  30.         android:title="@string/wifi_settings_title" />  
  31.   
  32.     <!-- Bluetooth -->  
  33.     <header  
  34.         android:id="@+id/bluetooth_settings"  
  35.         android:fragment="com.android.settings.bluetooth.BluetoothSettings"  
  36.         android:icon="@drawable/ic_settings_bluetooth2"  
  37.         android:title="@string/bluetooth_settings_title" />  
  38.   
  39.     <!-- Data Usage -->  
  40.     <header  
  41.         android:id="@+id/data_usage_settings"  
  42.         android:fragment="com.android.settings.DataUsageSummary"  
  43.         android:icon="@drawable/ic_settings_data_usage"  
  44.         android:title="@string/data_usage_summary_title" />  
  45.   
  46.     <!-- Operator hook -->  
  47.     <header  
  48.         android:id="@+id/operator_settings"  
  49.         android:fragment="com.android.settings.WirelessSettings" >  
  50.         <intent android:action="com.android.settings.OPERATOR_APPLICATION_SETTING" />  
  51.     </header>  
  52.   
  53.     <!-- Other wireless and network controls -->  
  54.     <header  
  55.         android:id="@+id/wireless_settings"  
  56.         android:breadCrumbTitle="@string/wireless_networks_settings_title"  
  57.         android:fragment="com.android.settings.WirelessSettings"  
  58.         android:icon="@drawable/empty_icon"  
  59.         android:title="@string/radio_controls_title" />  
  60.   
  61.     <!-- DEVICE -->  
  62.     <header  
  63.         android:id="@+id/device_section"  
  64.         android:title="@string/header_category_device" />  
  65.   
  66.     <!-- Sound -->  
  67.     <header  
  68.         android:id="@+id/sound_settings"  
  69.         android:fragment="com.android.settings.SoundSettings"  
  70.         android:icon="@drawable/ic_settings_sound"  
  71.         android:title="@string/sound_settings" />  
  72.   
  73.     <!-- Display -->  
  74.     <header  
  75.         android:id="@+id/display_settings"  
  76.         android:fragment="com.android.settings.DisplaySettings"  
  77.         android:icon="@drawable/ic_settings_display"  
  78.         android:title="@string/display_settings" />  
  79.   
  80.     <!-- Storage -->  
  81.     <header  
  82.         android:id="@+id/storage_settings"  
  83.         android:fragment="com.android.settings.deviceinfo.Memory"  
  84.         android:icon="@drawable/ic_settings_storage"  
  85.         android:title="@string/storage_settings" />  
  86.   
  87.     <!-- Battery -->  
  88.     <header  
  89.         android:id="@+id/battery_settings"  
  90.         android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"  
  91.         android:icon="@drawable/ic_settings_battery"  
  92.         android:title="@string/power_usage_summary_title" />  
  93.   
  94.     <!-- Application Settings -->  
  95.     <header  
  96.         android:id="@+id/application_settings"  
  97.         android:fragment="com.android.settings.applications.ManageApplications"  
  98.         android:icon="@drawable/ic_settings_applications"  
  99.         android:title="@string/applications_settings" />  
  100.   
  101.     <!-- Manage users -->  
  102.     <header  
  103.         android:id="@+id/user_settings"  
  104.         android:fragment="com.android.settings.users.UserSettings"  
  105.         android:icon="@drawable/ic_settings_multiuser"  
  106.         android:title="@string/user_settings_title" />  
  107.   
  108.     <!-- Manufacturer hook -->  
  109.     <header  
  110.         android:id="@+id/manufacturer_settings"  
  111.         android:fragment="com.android.settings.WirelessSettings" >  
  112.         <intent android:action="com.android.settings.MANUFACTURER_APPLICATION_SETTING" />  
  113.     </header>  
  114.   
  115.     <!-- PERSONAL -->  
  116.     <header  
  117.         android:id="@+id/personal_section"  
  118.         android:title="@string/header_category_personal" />  
  119.   
  120.     <!-- Location -->  
  121.     <header  
  122.         android:id="@+id/location_settings"  
  123.         android:fragment="com.android.settings.LocationSettings"  
  124.         android:icon="@drawable/ic_settings_location"  
  125.         android:title="@string/location_settings_title" />  
  126.   
  127.     <!-- Security -->  
  128.     <header  
  129.         android:id="@+id/security_settings"  
  130.         android:fragment="com.android.settings.SecuritySettings"  
  131.         android:icon="@drawable/ic_settings_security"  
  132.         android:title="@string/security_settings_title" />  
  133.   
  134.     <!-- Language -->  
  135.     <header  
  136.         android:id="@+id/language_settings"  
  137.         android:fragment="com.android.settings.inputmethod.InputMethodAndLanguageSettings"  
  138.         android:icon="@drawable/ic_settings_language"  
  139.         android:title="@string/language_settings" />  
  140.   
  141.     <!-- Backup and reset -->  
  142.     <header  
  143.         android:id="@+id/privacy_settings"  
  144.         android:fragment="com.android.settings.PrivacySettings"  
  145.         android:icon="@drawable/ic_settings_backup"  
  146.         android:title="@string/privacy_settings" />  
  147.     <header  
  148.         android:id="@+id/kytusers_settings"  
  149.         android:fragment="com.android.settings.KytUsersSettings"  
  150.         android:icon="@drawable/ic_settings_backup"  
  151.         android:title="考易通賬戶" />  
  152.   
  153.     <!-- ACCOUNTS section -->  
  154.     <header  
  155.         android:id="@+id/account_settings"  
  156.         android:title="@string/account_settings" />  
  157.     <header  
  158.         android:id="@+id/account_add"  
  159.         android:icon="@drawable/ic_menu_add"  
  160.         android:title="@string/add_account_label" >  
  161.         <intent android:action="android.settings.ADD_ACCOUNT_SETTINGS" />  
  162.     </header>  
  163.   
  164.     <!-- SYSTEM -->  
  165.     <header  
  166.         android:id="@+id/system_section"  
  167.         android:title="@string/header_category_system" />  
  168.   
  169.     <!-- Date & Time -->  
  170.     <header  
  171.         android:id="@+id/date_time_settings"  
  172.         android:fragment="com.android.settings.DateTimeSettings"  
  173.         android:icon="@drawable/ic_settings_date_time"  
  174.         android:title="@string/date_and_time_settings_title" />  
  175.   
  176.     <!-- Accessibility feedback -->  
  177.     <header  
  178.         android:id="@+id/accessibility_settings"  
  179.         android:fragment="com.android.settings.AccessibilitySettings"  
  180.         android:icon="@drawable/ic_settings_accessibility"  
  181.         android:title="@string/accessibility_settings" />  
  182.   
  183.     <!-- Development -->  
  184.     <header  
  185.         android:id="@+id/development_settings"  
  186.         android:fragment="com.android.settings.DevelopmentSettings"  
  187.         android:icon="@drawable/ic_settings_development"  
  188.         android:title="@string/development_settings_title" />  
  189.   
  190.     <!-- About Device -->  
  191.     <header  
  192.         android:id="@+id/about_settings"  
  193.         android:fragment="com.android.settings.DeviceInfoSettings"  
  194.         android:icon="@drawable/ic_settings_about"  
  195.         android:title="@string/about_settings" />  
  196.   
  197. </preference-headers>  
  這些header分別對應着各自的菜單,如果想要添加還是刪除就在這裏修改即可。比如我們不想要藍牙模塊了,那就直接把下面這個header刪除即可,添加的話類似。
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. <!-- Bluetooth -->  
  2.   <header  
  3.       android:id="@+id/bluetooth_settings"  
  4.       android:fragment="com.android.settings.bluetooth.BluetoothSettings"  
  5.       android:icon="@drawable/ic_settings_bluetooth2"  
  6.       android:title="@string/bluetooth_settings_title" />  
   如果是做添加操作的話,不要忘了創建你的PreferenceFragment,然後在header裏面添加id、fragment、icon、title等,如上面那樣。
  
二、修改顯示的應用
      先看一下應用顯示的部分:
  看一下在源碼中對應的包:

   應用顯示是一個滑動的界面,猜測是用ViewPager實現的,下面開始尋找實現。首先進入xml文件夾找到settings_headers,找到這一段代碼:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. <!-- Application Settings -->  
  2.  <header  
  3.      android:id="@+id/application_settings"  
  4.      android:fragment="com.android.settings.applications.ManageApplications"  
  5.      android:icon="@drawable/ic_settings_applications"  
  6.      android:title="@string/applications_settings" />  
  然後打開對應的fragment,ManageApplications。發現這個它是繼承於Fragment
  既然繼承於Fragment,那就直接定位到onCreate()方法,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. @Override  
  2.    public void onCreate(Bundle savedInstanceState) {  
  3.        super.onCreate(savedInstanceState);  
  4.   
  5.        setHasOptionsMenu(true);  
  6.   
  7.        mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());  
  8.        Intent intent = getActivity().getIntent();  
  9.        String action = intent.getAction();  
  10.        int defaultListType = LIST_TYPE_DOWNLOADED;  
  11.        String className = getArguments() != null  
  12.                ? getArguments().getString("classname") : null;  
  13.        if (className == null) {  
  14.            className = intent.getComponent().getClassName();  
  15.        }  
  16.        if (className.equals(RunningServicesActivity.class.getName())  
  17.                || className.endsWith(".RunningServices")) {  
  18.            defaultListType = LIST_TYPE_RUNNING;  
  19.        } else if (className.equals(StorageUseActivity.class.getName())  
  20.                || Intent.ACTION_MANAGE_PACKAGE_STORAGE.equals(action)  
  21.                || className.endsWith(".StorageUse")) {  
  22.            mSortOrder = SORT_ORDER_SIZE;  
  23.            defaultListType = LIST_TYPE_ALL;  
  24.        } else if (Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) {  
  25.            // Select the all-apps list, with the default sorting  
  26.            defaultListType = LIST_TYPE_ALL;  
  27.        }  
  28.   
  29.        if (savedInstanceState != null) {  
  30.            mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);  
  31.            int tmp = savedInstanceState.getInt(EXTRA_DEFAULT_LIST_TYPE, -1);  
  32.            if (tmp != -1) defaultListType = tmp;  
  33.            mShowBackground = savedInstanceState.getBoolean(EXTRA_SHOW_BACKGROUND, false);  
  34.        }  
  35.   
  36.        mDefaultListType = defaultListType;  
  37.   
  38.        final Intent containerIntent = new Intent().setComponent(  
  39.                StorageMeasurement.DEFAULT_CONTAINER_COMPONENT);  
  40.        getActivity().bindService(containerIntent, mContainerConnection, Context.BIND_AUTO_CREATE);  
  41.   
  42.        mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);  
  43.        mComputingSizeStr = getActivity().getText(R.string.computing_size);  
  44.   
  45.        TabInfo tab = new TabInfo(this, mApplicationsState,  
  46.                getActivity().getString(R.string.filter_apps_third_party),  
  47.                LIST_TYPE_DOWNLOADED, this, savedInstanceState);  
  48.        mTabs.add(tab);  
  49.   
  50.        if (!Environment.isExternalStorageEmulated()) {  
  51.            tab = new TabInfo(this, mApplicationsState,  
  52.                    getActivity().getString(R.string.filter_apps_onsdcard),  
  53.                    LIST_TYPE_SDCARD, this, savedInstanceState);  
  54.            mTabs.add(tab);  
  55.        }  
  56.   
  57.        tab = new TabInfo(this, mApplicationsState,  
  58.                getActivity().getString(R.string.filter_apps_running),  
  59.                LIST_TYPE_RUNNING, this, savedInstanceState);  
  60.        mTabs.add(tab);  
  61.   
  62.        tab = new TabInfo(this, mApplicationsState,  
  63.                getActivity().getString(R.string.filter_apps_all),  
  64.                LIST_TYPE_ALL, this, savedInstanceState);  
  65.        mTabs.add(tab);  
  66.    }  
  這裏主要是初始化TabInfo的數據,之後顯示程序的時候會用到。接下來定位到onCreateView()方法,這個方法主要是初始化界面,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. @Override  
  2.     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
  3.         // initialize the inflater  
  4.         mInflater = inflater;  
  5.   
  6.         View rootView = mInflater.inflate(R.layout.manage_applications_content,  
  7.                 container, false);  
  8.         mContentContainer = container;  
  9.         mRootView = rootView;  
  10.   
  11.         mViewPager = (ViewPager) rootView.findViewById(R.id.pager);  
  12.         MyPagerAdapter adapter = new MyPagerAdapter();  
  13.         mViewPager.setAdapter(adapter);  
  14.         mViewPager.setOnPageChangeListener(adapter);  
  15.         PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs);  
  16.         tabs.setTabIndicatorColorResource(android.R.color.holo_blue_light);  
  17.   
  18.         // We have to do this now because PreferenceFrameLayout looks at it  
  19.         // only when the view is added.  
  20.         if (container instanceof PreferenceFrameLayout) {  
  21.             ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true;  
  22.         }  
  23.   
  24.         if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) {  
  25.             buildResetDialog();  
  26.         }  
  27.   
  28.         if (savedInstanceState == null) {  
  29.             // First time init: make sure view pager is showing the correct tab.  
  30.             for (int i = 0; i < mTabs.size(); i++) {  
  31.                 TabInfo tab = mTabs.get(i);  
  32.                 if (tab.mListType == mDefaultListType) {  
  33.                     mViewPager.setCurrentItem(i);  
  34.                     break;  
  35.                 }  
  36.             }  
  37.         }  
  38.   
  39.         return rootView;  
  40.     }  
  可以看到,正是使用了ViewPager,另外還有PagerTabStrip。先看一下最下面的:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. if (savedInstanceState == null) {  
  2.           // First time init: make sure view pager is showing the correct tab.  
  3.           for (int i = 0; i < mTabs.size(); i++) {  
  4.               TabInfo tab = mTabs.get(i);  
  5.               if (tab.mListType == mDefaultListType) {  
  6.                   mViewPager.setCurrentItem(i);  
  7.                   break;  
  8.               }  
  9.           }  
  10.       }  
  這段代碼的作用就是用於設置默認顯示哪個選項卡的程序,所以就是“已下載”這個界面。然後回過頭看一下上面的代碼,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. MyPagerAdapter adapter = new MyPagerAdapter();  
  2.         mViewPager.setAdapter(adapter);  
  這個就是熟悉的適配器了,所以顯示程序的數據在這裏面。開打這個類,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. class MyPagerAdapter extends PagerAdapter  
  2.             implements ViewPager.OnPageChangeListener {  
  3.         int mCurPos = 0;  
  4.   
  5.         @Override  
  6.         public int getCount() {  
  7.             return mTabs.size();  
  8.         }  
  9.           
  10.         @Override  
  11.         public Object instantiateItem(ViewGroup container, int position) {  
  12.             TabInfo tab = mTabs.get(position);  
  13.             View root = tab.build(mInflater, mContentContainer, mRootView);  
  14.             container.addView(root);  
  15.             return root;  
  16.         }  
  17.   
  18.         @Override  
  19.         public void destroyItem(ViewGroup container, int position, Object object) {  
  20.             container.removeView((View)object);  
  21.         }  
  22.   
  23.         @Override  
  24.         public boolean isViewFromObject(View view, Object object) {  
  25.             return view == object;  
  26.         }  
  27.   
  28.         @Override  
  29.         public CharSequence getPageTitle(int position) {  
  30.             return mTabs.get(position).mLabel;  
  31.         }  
  32.   
  33.         @Override  
  34.         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  
  35.         }  
  36.   
  37.         @Override  
  38.         public void onPageSelected(int position) {  
  39.             mCurPos = position;  
  40.         }  
  41.   
  42.         @Override  
  43.         public void onPageScrollStateChanged(int state) {  
  44.             if (state == ViewPager.SCROLL_STATE_IDLE) {  
  45.                 updateCurrentTab(mCurPos);  
  46.             }  
  47.         }  
  48.     }  
  可以看到,有幾個滑動的頁卡,是通過mTabs這個類來控制的,而它就是TabInfo類型。接着看一下繪製視圖的方法:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. MyPagerAdapter  @Override  
  2.         public Object instantiateItem(ViewGroup container, int position) {  
  3.             TabInfo tab = mTabs.get(position);  
  4.             View root = tab.build(mInflater, mContentContainer, mRootView);  
  5.             container.addView(root);  
  6.             return root;  
  7.         }  
   首先是獲取到一個tab,然後這個tab用它的build()方法去生成一個視圖,最後放到ViewPager顯示。所以問題的重點分析就是TabInfo這個類了。先說buid()方法,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. public View build(LayoutInflater inflater, ViewGroup contentParent,  
  2.                View contentChild) {  
  3.            if (mRootView != null) {  
  4.                return mRootView;  
  5.            }  
  6.   
  7.            mInflater = inflater;  
  8.            mRootView = inflater  
  9.                    .inflate(  
  10.                            mListType == LIST_TYPE_RUNNING ? R.layout.manage_applications_running  
  11.                                    : R.layout.manage_applications_apps, null);  
  12.            mLoadingContainer = mRootView.findViewById(R.id.loading_container);  
  13.            mLoadingContainer.setVisibility(View.VISIBLE);  
  14.            mListContainer = mRootView.findViewById(R.id.list_container);  
  15.            if (mListContainer != null) {  
  16.                // Create adapter and list view here  
  17.                View emptyView = mListContainer  
  18.                        .findViewById(com.android.internal.R.id.empty);  
  19.                ListView lv = (ListView) mListContainer  
  20.                        .findViewById(android.R.id.list);  
  21.                if (emptyView != null) {  
  22.                    lv.setEmptyView(emptyView);  
  23.                }  
  24.                lv.setOnItemClickListener(this);  
  25.                lv.setSaveEnabled(true);  
  26.                lv.setItemsCanFocus(true);  
  27.                lv.setTextFilterEnabled(true);  
  28.                mListView = lv;  
  29.                mApplications = new ApplicationsAdapter(mApplicationsState,  
  30.                        this, mFilter);  
  31.                mListView.setAdapter(mApplications);  
  32.                mListView.setRecyclerListener(mApplications);  
  33.                mColorBar = (LinearColorBar) mListContainer  
  34.                        .findViewById(R.id.storage_color_bar);  
  35.                mStorageChartLabel = (TextView) mListContainer  
  36.                        .findViewById(R.id.storageChartLabel);  
  37.                mUsedStorageText = (TextView) mListContainer  
  38.                        .findViewById(R.id.usedStorageText);  
  39.                mFreeStorageText = (TextView) mListContainer  
  40.                        .findViewById(R.id.freeStorageText);  
  41.                Utils.prepareCustomPreferencesList(contentParent, contentChild,  
  42.                        mListView, false);  
  43.                if (mFilter == FILTER_APPS_SDCARD) {  
  44.                    mStorageChartLabel.setText(mOwner.getActivity().getText(  
  45.                            R.string.sd_card_storage));  
  46.                } else {  
  47.                    mStorageChartLabel.setText(mOwner.getActivity().getText(  
  48.                            R.string.internal_storage));  
  49.                }  
  50.                applyCurrentStorage();  
  51.            }  
  52.            mRunningProcessesView = (RunningProcessesView) mRootView  
  53.                    .findViewById(R.id.running_processes);  
  54.            if (mRunningProcessesView != null) {  
  55.                mRunningProcessesView.doCreate(mSavedInstanceState);  
  56.            }  
  57.   
  58.            return mRootView;  
  59.        }  
  第四行是生成了一view對象,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. mRootView = inflater.inflate(mListType == LIST_TYPE_RUNNING ? R.layout.manage_applications_running: R.layout.manage_applications_apps, null);  
  這行代碼用來判斷加載哪個佈局文件,確實viewpager雖然動態顯示四個頁面,但是其中三個的佈局是一樣的,唯一不一樣的就是顯示正在運行的界面。
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. ListView lv = (ListView) mListContainer  
  2.                         .findViewById(android.R.id.list);  
  3.                 if (emptyView != null) {  
  4.                     lv.setEmptyView(emptyView);  
  5.                 }  
  6.                 lv.setOnItemClickListener(this);  
  7.                 lv.setSaveEnabled(true);  
  8.                 lv.setItemsCanFocus(true);  
  9.                 lv.setTextFilterEnabled(true);  
  10.                 mListView = lv;  
  11.                 mApplications = new ApplicationsAdapter(mApplicationsState,  
  12.                         this, mFilter);  
  13.                 mListView.setAdapter(mApplications);  
  這段代碼就是用於顯示app程序了,然後定位到ApplicationsAdapter這個類,在它的構造方法裏面,傳入三個參數。一個是mApplicationsState對象,以後用於對ApplicationsState類進行操作;一個是TabInfo,用來顯示不同的界面;一個是過濾器,是標識顯示哪個界面。進入這個構造方法,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. public ApplicationsAdapter(ApplicationsState state, TabInfo tab,  
  2.              int filterMode) {  
  3.          mState = state;  
  4.          mSession = state.newSession(this);  
  5.          mTab = tab;  
  6.          mContext = tab.mOwner.getActivity();  
  7.          mFilterMode = filterMode;  
  8.      }  
   發現沒有在這裏傳入什麼數據,然後看一下ApplicationsAdapter這個類,發現它繼承了三個接口,第一個是過濾用的,第三個是系統SDK接口用於ListView循環處理。而第二個接口正是負責處理數據的,
  它有六個回調方法:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. public static interface Callbacks {  
  2.        public void onRunningStateChanged(boolean running);  
  3.   
  4.        public void onPackageListChanged();  
  5.   
  6.        public void onRebuildComplete(ArrayList<AppEntry> apps);  
  7.   
  8.        public void onPackageIconChanged();  
  9.   
  10.        public void onPackageSizeChanged(String packageName);  
  11.   
  12.        public void onAllSizesComputed();  
  13.    }  
  第三個回調方法onRebuildComplete(ArrayList<AppEntry> apps)正是用於返回app Entities的,但是到這裏如果我們還是按這條線是分析不下去了,找不到這個數據是從哪裏來的。所以不能按照這個思路往下走了,也就是說app程序數據不是在這裏獲取的,那會是什麼地方呢?一般情況下,我們是在onStart或者onCreat方法裏面,但是Setting裏面都沒有這樣做,那往下看一下onResume吧。果然Settings是在這個方法裏面加載數據的,
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. @Override  
  2. public void onResume() {  
  3.     super.onResume();  
  4.     mActivityResumed = true;  
  5.     updateCurrentTab(mViewPager.getCurrentItem());  
  6.     updateOptionsMenu();  
  7. }  
  首先是調用了updateCurrentTab(mViewPager.getCurrentItem())方法,然後updateCurrentTab方法裏面又調用了TabInfo的resume方法,在TabInfo的resume方法裏面接着調用了ApplicationsAdapter 的resume方法,又在ApplicationsAdapter 的resume方法裏面調用Session的resume方法,最後又在Session的resume方法裏面調用doResumeIfNeededLocked()方法,這個方法就是從系統讀取程序信息的,代碼如下:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void doResumeIfNeededLocked() {  
  2.         if (mResumed) {  
  3.             return;  
  4.         }  
  5.         mResumed = true;  
  6.         if (mPackageIntentReceiver == null) {  
  7.             mPackageIntentReceiver = new PackageIntentReceiver();  
  8.             mPackageIntentReceiver.registerReceiver();  
  9.         }  
  10.         //這個mApplications就是所有程序數據,如果你想過濾哪些程序的信息,對這個集合進行修改即可。比如你在做定製機的時候,不想自己的程序顯示在Settings裏面,那就在這裏修改。  
  11.         mApplications = mPm.getInstalledApplications(mRetrieveFlags);  
  12.         if (mApplications == null) {  
  13.             mApplications = new ArrayList<ApplicationInfo>();  
  14.         }  
  15.   
  16.         if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {  
  17.             // If an interesting part of the configuration has changed, we  
  18.             // should completely reload the app entries.  
  19.             mEntriesMap.clear();  
  20.             mAppEntries.clear();  
  21.         } else {  
  22.             for (int i = 0; i < mAppEntries.size(); i++) {  
  23.                 mAppEntries.get(i).sizeStale = true;  
  24.             }  
  25.         }  
  26.   
  27.         for (int i = 0; i < mApplications.size(); i++) {  
  28.             final ApplicationInfo info = mApplications.get(i);  
  29.             // Need to trim out any applications that are disabled by  
  30.             // something different than the user.  
  31.             if (!info.enabled  
  32.                     && info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {  
  33.                 mApplications.remove(i);  
  34.                 i--;  
  35.                 continue;  
  36.             }  
  37.             final AppEntry entry = mEntriesMap.get(info.packageName);  
  38.             if (entry != null) {  
  39.                 entry.info = info;  
  40.             }  
  41.         }  
  42.         mCurComputingSizePkg = null;  
  43.         if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {  
  44.             mBackgroundHandler  
  45.                     .sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);  
  46.         }  
  47.     };  
   至此,完成了顯示程序的剖析,其他模塊類似,只要按照流程走就行了。



發佈了21 篇原創文章 · 獲贊 39 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章