PreferenceFragment.java revision 42c2936f3c6e048caafb17eb9fe91fa4a33c8b86
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.preference; 18 19import android.app.Activity; 20import android.app.Fragment; 21import android.content.Intent; 22import android.content.SharedPreferences; 23import android.os.Bundle; 24import android.os.Handler; 25import android.os.Message; 26import android.view.LayoutInflater; 27import android.view.View; 28import android.view.ViewGroup; 29import android.widget.ListView; 30 31/** 32 * Shows a hierarchy of {@link Preference} objects as 33 * lists. These preferences will 34 * automatically save to {@link SharedPreferences} as the user interacts with 35 * them. To retrieve an instance of {@link SharedPreferences} that the 36 * preference hierarchy in this fragment will use, call 37 * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)} 38 * with a context in the same package as this fragment. 39 * <p> 40 * The preference hierarchy can be formed in multiple ways: 41 * <li> From an XML file specifying the hierarchy 42 * <li> From different {@link Activity Activities} that each specify its own 43 * preferences in an XML file via {@link Activity} meta-data 44 * <li> From an object hierarchy rooted with {@link PreferenceScreen} 45 * <p> 46 * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The 47 * root element should be a {@link PreferenceScreen}. Subsequent elements can point 48 * to actual {@link Preference} subclasses. As mentioned above, subsequent 49 * {@link PreferenceScreen} in the hierarchy will result in the screen break. 50 * <p> 51 * To specify an {@link Intent} to query {@link Activity Activities} that each 52 * have preferences, use {@link #addPreferencesFromIntent}. Each 53 * {@link Activity} can specify meta-data in the manifest (via the key 54 * {@link PreferenceManager#METADATA_KEY_PREFERENCES}) that points to an XML 55 * resource. These XML resources will be inflated into a single preference 56 * hierarchy and shown by this fragment. 57 * <p> 58 * To specify an object hierarchy rooted with {@link PreferenceScreen}, use 59 * {@link #setPreferenceScreen(PreferenceScreen)}. 60 * <p> 61 * As a convenience, this fragment implements a click listener for any 62 * preference in the current hierarchy, see 63 * {@link #onPreferenceTreeClick(PreferenceScreen, Preference)}. 64 * <p> 65 * See {@link PreferenceActivity} for more details. 66 * 67 * <a name="SampleCode"></a> 68 * <h3>Sample Code</h3> 69 * 70 * <p>The following sample code shows the use if a PreferenceFragment to 71 * embed preferences in a larger activity and switch between them. The content 72 * layout of the activity is:</p> 73 * 74 * {@sample development/samples/ApiDemos/res/layout/fragment_preferences.xml layout} 75 * 76 * <p>The code using this layout consists of an activity and three fragments. 77 * One of the fragments is a list of categories the user can select; the other 78 * two are the different preference options for the categories.</p> 79 * 80 * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentPreferences.java 81 * activity} 82 * 83 * @see Preference 84 * @see PreferenceScreen 85 */ 86public abstract class PreferenceFragment extends Fragment implements 87 PreferenceManager.OnPreferenceTreeClickListener { 88 89 private static final String PREFERENCES_TAG = "android:preferences"; 90 91 private PreferenceManager mPreferenceManager; 92 private ListView mList; 93 private boolean mHavePrefs; 94 private boolean mInitDone; 95 96 /** 97 * The starting request code given out to preference framework. 98 */ 99 private static final int FIRST_REQUEST_CODE = 100; 100 101 private static final int MSG_BIND_PREFERENCES = 0; 102 private Handler mHandler = new Handler() { 103 @Override 104 public void handleMessage(Message msg) { 105 switch (msg.what) { 106 107 case MSG_BIND_PREFERENCES: 108 bindPreferences(); 109 break; 110 } 111 } 112 }; 113 114 final private Runnable mRequestFocus = new Runnable() { 115 public void run() { 116 mList.focusableViewAvailable(mList); 117 } 118 }; 119 120 @Override 121 public void onCreate(Bundle savedInstanceState) { 122 super.onCreate(savedInstanceState); 123 mPreferenceManager = new PreferenceManager(getActivity(), FIRST_REQUEST_CODE); 124 mPreferenceManager.setOnPreferenceTreeClickListener(this); 125 } 126 127 @Override 128 public View onCreateView(LayoutInflater inflater, ViewGroup container, 129 Bundle savedInstanceState) { 130 return inflater.inflate(com.android.internal.R.layout.preference_list_content, 131 container, false); 132 } 133 134 @Override 135 public void onActivityCreated(Bundle savedInstanceState) { 136 super.onActivityCreated(savedInstanceState); 137 getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); 138 139 if (mHavePrefs) { 140 bindPreferences(); 141 } 142 143 mInitDone = true; 144 145 if (savedInstanceState != null) { 146 Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG); 147 if (container != null) { 148 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 149 if (preferenceScreen != null) { 150 preferenceScreen.restoreHierarchyState(container); 151 } 152 } 153 } 154 } 155 156 @Override 157 public void onStop() { 158 super.onStop(); 159 mPreferenceManager.dispatchActivityStop(); 160 } 161 162 @Override 163 public void onDestroy() { 164 super.onDestroy(); 165 mPreferenceManager.dispatchActivityDestroy(); 166 } 167 168 @Override 169 public void onSaveInstanceState(Bundle outState) { 170 super.onSaveInstanceState(outState); 171 172 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 173 if (preferenceScreen != null) { 174 Bundle container = new Bundle(); 175 preferenceScreen.saveHierarchyState(container); 176 outState.putBundle(PREFERENCES_TAG, container); 177 } 178 } 179 180 @Override 181 public void onActivityResult(int requestCode, int resultCode, Intent data) { 182 super.onActivityResult(requestCode, resultCode, data); 183 184 mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data); 185 } 186 187 /** 188 * Returns the {@link PreferenceManager} used by this fragment. 189 * @return The {@link PreferenceManager}. 190 */ 191 public PreferenceManager getPreferenceManager() { 192 return mPreferenceManager; 193 } 194 195 /** 196 * Sets the root of the preference hierarchy that this fragment is showing. 197 * 198 * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. 199 */ 200 public void setPreferenceScreen(PreferenceScreen preferenceScreen) { 201 if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) { 202 mHavePrefs = true; 203 if (mInitDone) { 204 postBindPreferences(); 205 } 206 } 207 } 208 209 /** 210 * Gets the root of the preference hierarchy that this fragment is showing. 211 * 212 * @return The {@link PreferenceScreen} that is the root of the preference 213 * hierarchy. 214 */ 215 public PreferenceScreen getPreferenceScreen() { 216 return mPreferenceManager.getPreferenceScreen(); 217 } 218 219 /** 220 * Adds preferences from activities that match the given {@link Intent}. 221 * 222 * @param intent The {@link Intent} to query activities. 223 */ 224 public void addPreferencesFromIntent(Intent intent) { 225 requirePreferenceManager(); 226 227 setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen())); 228 } 229 230 /** 231 * Inflates the given XML resource and adds the preference hierarchy to the current 232 * preference hierarchy. 233 * 234 * @param preferencesResId The XML resource ID to inflate. 235 */ 236 public void addPreferencesFromResource(int preferencesResId) { 237 requirePreferenceManager(); 238 239 setPreferenceScreen(mPreferenceManager.inflateFromResource(getActivity(), 240 preferencesResId, getPreferenceScreen())); 241 } 242 243 /** 244 * {@inheritDoc} 245 */ 246 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 247 return false; 248 } 249 250 /** 251 * Finds a {@link Preference} based on its key. 252 * 253 * @param key The key of the preference to retrieve. 254 * @return The {@link Preference} with the key, or null. 255 * @see PreferenceGroup#findPreference(CharSequence) 256 */ 257 public Preference findPreference(CharSequence key) { 258 if (mPreferenceManager == null) { 259 return null; 260 } 261 return mPreferenceManager.findPreference(key); 262 } 263 264 private void requirePreferenceManager() { 265 if (mPreferenceManager == null) { 266 throw new RuntimeException("This should be called after super.onCreate."); 267 } 268 } 269 270 private void postBindPreferences() { 271 if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return; 272 mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); 273 } 274 275 private void bindPreferences() { 276 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 277 if (preferenceScreen != null) { 278 preferenceScreen.bind(getListView()); 279 } 280 } 281 282 private ListView getListView() { 283 ensureList(); 284 return mList; 285 } 286 287 private void ensureList() { 288 if (mList != null) { 289 return; 290 } 291 View root = getView(); 292 if (root == null) { 293 throw new IllegalStateException("Content view not yet created"); 294 } 295 View rawListView = root.findViewById(android.R.id.list); 296 if (!(rawListView instanceof ListView)) { 297 throw new RuntimeException( 298 "Content has view with id attribute 'android.R.id.list' " 299 + "that is not a ListView class"); 300 } 301 mList = (ListView)rawListView; 302 if (mList == null) { 303 throw new RuntimeException( 304 "Your content must have a ListView whose id attribute is " + 305 "'android.R.id.list'"); 306 } 307 mHandler.post(mRequestFocus); 308 } 309} 310