The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2006 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 | |
| 17 | package android.app; |
| 18 | |
| 19 | import android.database.Cursor; |
| 20 | import android.os.Bundle; |
| 21 | import java.util.List; |
| 22 | import android.view.ContextMenu; |
| 23 | import android.view.View; |
| 24 | import android.view.ContextMenu.ContextMenuInfo; |
| 25 | import android.view.View.OnCreateContextMenuListener; |
| 26 | import android.widget.ExpandableListAdapter; |
| 27 | import android.widget.ExpandableListView; |
| 28 | import android.widget.SimpleCursorTreeAdapter; |
| 29 | import android.widget.SimpleExpandableListAdapter; |
| 30 | import android.widget.AdapterView.AdapterContextMenuInfo; |
| 31 | |
| 32 | import java.util.Map; |
| 33 | |
| 34 | /** |
| 35 | * An activity that displays an expandable list of items by binding to a data |
| 36 | * source implementing the ExpandableListAdapter, and exposes event handlers |
| 37 | * when the user selects an item. |
| 38 | * <p> |
| 39 | * ExpandableListActivity hosts a |
| 40 | * {@link android.widget.ExpandableListView ExpandableListView} object that can |
| 41 | * be bound to different data sources that provide a two-levels of data (the |
| 42 | * top-level is group, and below each group are children). Binding, screen |
| 43 | * layout, and row layout are discussed in the following sections. |
| 44 | * <p> |
| 45 | * <strong>Screen Layout</strong> |
| 46 | * </p> |
| 47 | * <p> |
| 48 | * ExpandableListActivity has a default layout that consists of a single, |
| 49 | * full-screen, centered expandable list. However, if you desire, you can |
| 50 | * customize the screen layout by setting your own view layout with |
| 51 | * setContentView() in onCreate(). To do this, your own view MUST contain an |
| 52 | * ExpandableListView object with the id "@android:id/list" (or |
| 53 | * {@link android.R.id#list} if it's in code) |
| 54 | * <p> |
| 55 | * Optionally, your custom view can contain another view object of any type to |
| 56 | * display when the list view is empty. This "empty list" notifier must have an |
| 57 | * id "android:empty". Note that when an empty view is present, the expandable |
| 58 | * list view will be hidden when there is no data to display. |
| 59 | * <p> |
| 60 | * The following code demonstrates an (ugly) custom screen layout. It has a list |
| 61 | * with a green background, and an alternate red "no data" message. |
| 62 | * </p> |
| 63 | * |
| 64 | * <pre> |
| 65 | * <?xml version="1.0" encoding="UTF-8"?> |
| 66 | * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 67 | * android:orientation="vertical" |
Romain Guy | 980a938 | 2010-01-08 15:06:28 -0800 | [diff] [blame] | 68 | * android:layout_width="match_parent" |
| 69 | * android:layout_height="match_parent" |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 70 | * android:paddingLeft="8dp" |
| 71 | * android:paddingRight="8dp"> |
| 72 | * |
| 73 | * <ExpandableListView android:id="@id/android:list" |
Romain Guy | 980a938 | 2010-01-08 15:06:28 -0800 | [diff] [blame] | 74 | * android:layout_width="match_parent" |
| 75 | * android:layout_height="match_parent" |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 76 | * android:background="#00FF00" |
| 77 | * android:layout_weight="1" |
| 78 | * android:drawSelectorOnTop="false"/> |
| 79 | * |
| 80 | * <TextView android:id="@id/android:empty" |
Romain Guy | 980a938 | 2010-01-08 15:06:28 -0800 | [diff] [blame] | 81 | * android:layout_width="match_parent" |
| 82 | * android:layout_height="match_parent" |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 83 | * android:background="#FF0000" |
| 84 | * android:text="No data"/> |
| 85 | * </LinearLayout> |
| 86 | * </pre> |
| 87 | * |
| 88 | * <p> |
| 89 | * <strong>Row Layout</strong> |
| 90 | * </p> |
| 91 | * The {@link ExpandableListAdapter} set in the {@link ExpandableListActivity} |
| 92 | * via {@link #setListAdapter(ExpandableListAdapter)} provides the {@link View}s |
| 93 | * for each row. This adapter has separate methods for providing the group |
| 94 | * {@link View}s and child {@link View}s. There are a couple provided |
| 95 | * {@link ExpandableListAdapter}s that simplify use of adapters: |
| 96 | * {@link SimpleCursorTreeAdapter} and {@link SimpleExpandableListAdapter}. |
| 97 | * <p> |
| 98 | * With these, you can specify the layout of individual rows for groups and |
| 99 | * children in the list. These constructor takes a few parameters that specify |
| 100 | * layout resources for groups and children. It also has additional parameters |
| 101 | * that let you specify which data field to associate with which object in the |
| 102 | * row layout resource. The {@link SimpleCursorTreeAdapter} fetches data from |
| 103 | * {@link Cursor}s and the {@link SimpleExpandableListAdapter} fetches data |
| 104 | * from {@link List}s of {@link Map}s. |
| 105 | * </p> |
| 106 | * <p> |
| 107 | * Android provides some standard row layout resources. These are in the |
| 108 | * {@link android.R.layout} class, and have names such as simple_list_item_1, |
| 109 | * simple_list_item_2, and two_line_list_item. The following layout XML is the |
| 110 | * source for the resource two_line_list_item, which displays two data |
| 111 | * fields,one above the other, for each list row. |
| 112 | * </p> |
| 113 | * |
| 114 | * <pre> |
| 115 | * <?xml version="1.0" encoding="utf-8"?> |
| 116 | * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
Romain Guy | 980a938 | 2010-01-08 15:06:28 -0800 | [diff] [blame] | 117 | * android:layout_width="match_parent" |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 118 | * android:layout_height="wrap_content" |
| 119 | * android:orientation="vertical"> |
| 120 | * |
| 121 | * <TextView android:id="@+id/text1" |
| 122 | * android:textSize="16sp" |
| 123 | * android:textStyle="bold" |
Romain Guy | 980a938 | 2010-01-08 15:06:28 -0800 | [diff] [blame] | 124 | * android:layout_width="match_parent" |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 125 | * android:layout_height="wrap_content"/> |
| 126 | * |
| 127 | * <TextView android:id="@+id/text2" |
| 128 | * android:textSize="16sp" |
Romain Guy | 980a938 | 2010-01-08 15:06:28 -0800 | [diff] [blame] | 129 | * android:layout_width="match_parent" |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 130 | * android:layout_height="wrap_content"/> |
| 131 | * </LinearLayout> |
| 132 | * </pre> |
| 133 | * |
| 134 | * <p> |
| 135 | * You must identify the data bound to each TextView object in this layout. The |
| 136 | * syntax for this is discussed in the next section. |
| 137 | * </p> |
| 138 | * <p> |
| 139 | * <strong>Binding to Data</strong> |
| 140 | * </p> |
| 141 | * <p> |
| 142 | * You bind the ExpandableListActivity's ExpandableListView object to data using |
| 143 | * a class that implements the |
| 144 | * {@link android.widget.ExpandableListAdapter ExpandableListAdapter} interface. |
| 145 | * Android provides two standard list adapters: |
| 146 | * {@link android.widget.SimpleExpandableListAdapter SimpleExpandableListAdapter} |
| 147 | * for static data (Maps), and |
| 148 | * {@link android.widget.SimpleCursorTreeAdapter SimpleCursorTreeAdapter} for |
| 149 | * Cursor query results. |
| 150 | * </p> |
| 151 | * |
| 152 | * @see #setListAdapter |
| 153 | * @see android.widget.ExpandableListView |
| 154 | */ |
| 155 | public class ExpandableListActivity extends Activity implements |
| 156 | OnCreateContextMenuListener, |
| 157 | ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupCollapseListener, |
| 158 | ExpandableListView.OnGroupExpandListener { |
| 159 | ExpandableListAdapter mAdapter; |
| 160 | ExpandableListView mList; |
| 161 | boolean mFinishedStart = false; |
| 162 | |
| 163 | /** |
| 164 | * Override this to populate the context menu when an item is long pressed. menuInfo |
| 165 | * will contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo} |
| 166 | * whose packedPosition is a packed position |
| 167 | * that should be used with {@link ExpandableListView#getPackedPositionType(long)} and |
| 168 | * the other similar methods. |
| 169 | * <p> |
| 170 | * {@inheritDoc} |
| 171 | */ |
| 172 | @Override |
| 173 | public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { |
| 174 | } |
| 175 | |
| 176 | /** |
| 177 | * Override this for receiving callbacks when a child has been clicked. |
| 178 | * <p> |
| 179 | * {@inheritDoc} |
| 180 | */ |
| 181 | public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, |
| 182 | int childPosition, long id) { |
| 183 | return false; |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Override this for receiving callbacks when a group has been collapsed. |
| 188 | */ |
| 189 | public void onGroupCollapse(int groupPosition) { |
| 190 | } |
| 191 | |
| 192 | /** |
| 193 | * Override this for receiving callbacks when a group has been expanded. |
| 194 | */ |
| 195 | public void onGroupExpand(int groupPosition) { |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Ensures the expandable list view has been created before Activity restores all |
| 200 | * of the view states. |
| 201 | * |
| 202 | *@see Activity#onRestoreInstanceState(Bundle) |
| 203 | */ |
| 204 | @Override |
| 205 | protected void onRestoreInstanceState(Bundle state) { |
| 206 | ensureList(); |
| 207 | super.onRestoreInstanceState(state); |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Updates the screen state (current list and other views) when the |
| 212 | * content changes. |
| 213 | * |
| 214 | * @see Activity#onContentChanged() |
| 215 | */ |
| 216 | @Override |
| 217 | public void onContentChanged() { |
| 218 | super.onContentChanged(); |
| 219 | View emptyView = findViewById(com.android.internal.R.id.empty); |
| 220 | mList = (ExpandableListView)findViewById(com.android.internal.R.id.list); |
| 221 | if (mList == null) { |
| 222 | throw new RuntimeException( |
| 223 | "Your content must have a ExpandableListView whose id attribute is " + |
| 224 | "'android.R.id.list'"); |
| 225 | } |
| 226 | if (emptyView != null) { |
| 227 | mList.setEmptyView(emptyView); |
| 228 | } |
| 229 | mList.setOnChildClickListener(this); |
| 230 | mList.setOnGroupExpandListener(this); |
| 231 | mList.setOnGroupCollapseListener(this); |
| 232 | |
| 233 | if (mFinishedStart) { |
| 234 | setListAdapter(mAdapter); |
| 235 | } |
| 236 | mFinishedStart = true; |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Provide the adapter for the expandable list. |
| 241 | */ |
| 242 | public void setListAdapter(ExpandableListAdapter adapter) { |
| 243 | synchronized (this) { |
| 244 | ensureList(); |
| 245 | mAdapter = adapter; |
| 246 | mList.setAdapter(adapter); |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | /** |
| 251 | * Get the activity's expandable list view widget. This can be used to get the selection, |
| 252 | * set the selection, and many other useful functions. |
| 253 | * |
| 254 | * @see ExpandableListView |
| 255 | */ |
| 256 | public ExpandableListView getExpandableListView() { |
| 257 | ensureList(); |
| 258 | return mList; |
| 259 | } |
| 260 | |
| 261 | /** |
| 262 | * Get the ExpandableListAdapter associated with this activity's |
| 263 | * ExpandableListView. |
| 264 | */ |
| 265 | public ExpandableListAdapter getExpandableListAdapter() { |
| 266 | return mAdapter; |
| 267 | } |
| 268 | |
| 269 | private void ensureList() { |
| 270 | if (mList != null) { |
| 271 | return; |
| 272 | } |
| 273 | setContentView(com.android.internal.R.layout.expandable_list_content); |
| 274 | } |
| 275 | |
| 276 | /** |
| 277 | * Gets the ID of the currently selected group or child. |
| 278 | * |
| 279 | * @return The ID of the currently selected group or child. |
| 280 | */ |
| 281 | public long getSelectedId() { |
| 282 | return mList.getSelectedId(); |
| 283 | } |
| 284 | |
| 285 | /** |
| 286 | * Gets the position (in packed position representation) of the currently |
| 287 | * selected group or child. Use |
| 288 | * {@link ExpandableListView#getPackedPositionType}, |
| 289 | * {@link ExpandableListView#getPackedPositionGroup}, and |
| 290 | * {@link ExpandableListView#getPackedPositionChild} to unpack the returned |
| 291 | * packed position. |
| 292 | * |
| 293 | * @return A packed position representation containing the currently |
| 294 | * selected group or child's position and type. |
| 295 | */ |
| 296 | public long getSelectedPosition() { |
| 297 | return mList.getSelectedPosition(); |
| 298 | } |
| 299 | |
| 300 | /** |
| 301 | * Sets the selection to the specified child. If the child is in a collapsed |
| 302 | * group, the group will only be expanded and child subsequently selected if |
| 303 | * shouldExpandGroup is set to true, otherwise the method will return false. |
| 304 | * |
| 305 | * @param groupPosition The position of the group that contains the child. |
| 306 | * @param childPosition The position of the child within the group. |
| 307 | * @param shouldExpandGroup Whether the child's group should be expanded if |
| 308 | * it is collapsed. |
| 309 | * @return Whether the selection was successfully set on the child. |
| 310 | */ |
| 311 | public boolean setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup) { |
| 312 | return mList.setSelectedChild(groupPosition, childPosition, shouldExpandGroup); |
| 313 | } |
| 314 | |
| 315 | /** |
| 316 | * Sets the selection to the specified group. |
| 317 | * @param groupPosition The position of the group that should be selected. |
| 318 | */ |
| 319 | public void setSelectedGroup(int groupPosition) { |
| 320 | mList.setSelectedGroup(groupPosition); |
| 321 | } |
| 322 | |
| 323 | } |
| 324 | |