PreferenceScreen.java revision f9638a4aeecf23079fa74618ad3c285a8a2abfda
1/* 2 * Copyright (C) 2007 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.Dialog; 20import android.content.Context; 21import android.content.DialogInterface; 22import android.os.Bundle; 23import android.os.Parcel; 24import android.os.Parcelable; 25import android.text.TextUtils; 26import android.util.AttributeSet; 27import android.view.LayoutInflater; 28import android.view.View; 29import android.view.Window; 30import android.widget.AbsListView; 31import android.widget.Adapter; 32import android.widget.AdapterView; 33import android.widget.ListAdapter; 34import android.widget.ListView; 35 36/** 37 * Represents a top-level {@link Preference} that 38 * is the root of a Preference hierarchy. A {@link PreferenceActivity} 39 * points to an instance of this class to show the preferences. To instantiate 40 * this class, use {@link PreferenceManager#createPreferenceScreen(Context)}. 41 * <ul> 42 * This class can appear in two places: 43 * <li> When a {@link PreferenceActivity} points to this, it is used as the root 44 * and is not shown (only the contained preferences are shown). 45 * <li> When it appears inside another preference hierarchy, it is shown and 46 * serves as the gateway to another screen of preferences (either by showing 47 * another screen of preferences as a {@link Dialog} or via a 48 * {@link Context#startActivity(android.content.Intent)} from the 49 * {@link Preference#getIntent()}). The children of this {@link PreferenceScreen} 50 * are NOT shown in the screen that this {@link PreferenceScreen} is shown in. 51 * Instead, a separate screen will be shown when this preference is clicked. 52 * </ul> 53 * <p>Here's an example XML layout of a PreferenceScreen:</p> 54 * <pre> 55<PreferenceScreen 56 xmlns:android="http://schemas.android.com/apk/res/android" 57 android:key="first_preferencescreen"> 58 <CheckBoxPreference 59 android:key="wifi enabled" 60 android:title="WiFi" /> 61 <PreferenceScreen 62 android:key="second_preferencescreen" 63 android:title="WiFi settings"> 64 <CheckBoxPreference 65 android:key="prefer wifi" 66 android:title="Prefer WiFi" /> 67 ... other preferences here ... 68 </PreferenceScreen> 69</PreferenceScreen> </pre> 70 * <p> 71 * In this example, the "first_preferencescreen" will be used as the root of the 72 * hierarchy and given to a {@link PreferenceActivity}. The first screen will 73 * show preferences "WiFi" (which can be used to quickly enable/disable WiFi) 74 * and "WiFi settings". The "WiFi settings" is the "second_preferencescreen" and when 75 * clicked will show another screen of preferences such as "Prefer WiFi" (and 76 * the other preferences that are children of the "second_preferencescreen" tag). 77 * 78 * @see PreferenceCategory 79 */ 80public final class PreferenceScreen extends PreferenceGroup implements AdapterView.OnItemClickListener, 81 DialogInterface.OnDismissListener { 82 83 private ListAdapter mRootAdapter; 84 85 private Dialog mDialog; 86 87 private ListView mListView; 88 89 /** 90 * Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}. 91 * @hide- 92 */ 93 public PreferenceScreen(Context context, AttributeSet attrs) { 94 super(context, attrs, com.android.internal.R.attr.preferenceScreenStyle); 95 } 96 97 /** 98 * Returns an adapter that can be attached to a {@link PreferenceActivity} 99 * or {@link PreferenceFragment} to show the preferences contained in this 100 * {@link PreferenceScreen}. 101 * <p> 102 * This {@link PreferenceScreen} will NOT appear in the returned adapter, instead 103 * it appears in the hierarchy above this {@link PreferenceScreen}. 104 * <p> 105 * This adapter's {@link Adapter#getItem(int)} should always return a 106 * subclass of {@link Preference}. 107 * 108 * @return An adapter that provides the {@link Preference} contained in this 109 * {@link PreferenceScreen}. 110 */ 111 public ListAdapter getRootAdapter() { 112 if (mRootAdapter == null) { 113 mRootAdapter = onCreateRootAdapter(); 114 } 115 116 return mRootAdapter; 117 } 118 119 /** 120 * Creates the root adapter. 121 * 122 * @return An adapter that contains the preferences contained in this {@link PreferenceScreen}. 123 * @see #getRootAdapter() 124 */ 125 protected ListAdapter onCreateRootAdapter() { 126 return new PreferenceGroupAdapter(this); 127 } 128 129 /** 130 * Binds a {@link ListView} to the preferences contained in this {@link PreferenceScreen} via 131 * {@link #getRootAdapter()}. It also handles passing list item clicks to the corresponding 132 * {@link Preference} contained by this {@link PreferenceScreen}. 133 * 134 * @param listView The list view to attach to. 135 */ 136 public void bind(ListView listView) { 137 listView.setOnItemClickListener(this); 138 listView.setAdapter(getRootAdapter()); 139 140 onAttachedToActivity(); 141 } 142 143 @Override 144 protected void onClick() { 145 if (getIntent() != null || getFragment() != null || getPreferenceCount() == 0) { 146 return; 147 } 148 149 showDialog(null); 150 } 151 152 private void showDialog(Bundle state) { 153 Context context = getContext(); 154 if (mListView != null) { 155 mListView.setAdapter(null); 156 } 157 158 LayoutInflater inflater = (LayoutInflater) 159 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 160 View childPrefScreen = inflater.inflate( 161 com.android.internal.R.layout.preference_list_fragment, null); 162 mListView = (ListView) childPrefScreen.findViewById(android.R.id.list); 163 bind(mListView); 164 165 // Set the title bar if title is available, else no title bar 166 final CharSequence title = getTitle(); 167 Dialog dialog = mDialog = new Dialog(context, context.getThemeResId()); 168 if (TextUtils.isEmpty(title)) { 169 dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); 170 } else { 171 dialog.setTitle(title); 172 } 173 dialog.setContentView(childPrefScreen); 174 dialog.setOnDismissListener(this); 175 if (state != null) { 176 dialog.onRestoreInstanceState(state); 177 } 178 179 // Add the screen to the list of preferences screens opened as dialogs 180 getPreferenceManager().addPreferencesScreen(dialog); 181 182 dialog.show(); 183 } 184 185 public void onDismiss(DialogInterface dialog) { 186 mDialog = null; 187 getPreferenceManager().removePreferencesScreen(dialog); 188 } 189 190 /** 191 * Used to get a handle to the dialog. 192 * This is useful for cases where we want to manipulate the dialog 193 * as we would with any other activity or view. 194 */ 195 public Dialog getDialog() { 196 return mDialog; 197 } 198 199 public void onItemClick(AdapterView parent, View view, int position, long id) { 200 // If the list has headers, subtract them from the index. 201 if (parent instanceof ListView) { 202 position -= ((ListView) parent).getHeaderViewsCount(); 203 } 204 Object item = getRootAdapter().getItem(position); 205 if (!(item instanceof Preference)) return; 206 207 final Preference preference = (Preference) item; 208 preference.performClick(this); 209 } 210 211 @Override 212 protected boolean isOnSameScreenAsChildren() { 213 return false; 214 } 215 216 @Override 217 protected Parcelable onSaveInstanceState() { 218 final Parcelable superState = super.onSaveInstanceState(); 219 final Dialog dialog = mDialog; 220 if (dialog == null || !dialog.isShowing()) { 221 return superState; 222 } 223 224 final SavedState myState = new SavedState(superState); 225 myState.isDialogShowing = true; 226 myState.dialogBundle = dialog.onSaveInstanceState(); 227 return myState; 228 } 229 230 @Override 231 protected void onRestoreInstanceState(Parcelable state) { 232 if (state == null || !state.getClass().equals(SavedState.class)) { 233 // Didn't save state for us in onSaveInstanceState 234 super.onRestoreInstanceState(state); 235 return; 236 } 237 238 SavedState myState = (SavedState) state; 239 super.onRestoreInstanceState(myState.getSuperState()); 240 if (myState.isDialogShowing) { 241 showDialog(myState.dialogBundle); 242 } 243 } 244 245 private static class SavedState extends BaseSavedState { 246 boolean isDialogShowing; 247 Bundle dialogBundle; 248 249 public SavedState(Parcel source) { 250 super(source); 251 isDialogShowing = source.readInt() == 1; 252 dialogBundle = source.readBundle(); 253 } 254 255 @Override 256 public void writeToParcel(Parcel dest, int flags) { 257 super.writeToParcel(dest, flags); 258 dest.writeInt(isDialogShowing ? 1 : 0); 259 dest.writeBundle(dialogBundle); 260 } 261 262 public SavedState(Parcelable superState) { 263 super(superState); 264 } 265 266 public static final Parcelable.Creator<SavedState> CREATOR = 267 new Parcelable.Creator<SavedState>() { 268 public SavedState createFromParcel(Parcel in) { 269 return new SavedState(in); 270 } 271 272 public SavedState[] newArray(int size) { 273 return new SavedState[size]; 274 } 275 }; 276 } 277 278} 279