2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 import java.util.ArrayList;
20 import java.util.List;
22 import android.view.Gravity;
23 import android.view.View;
24 import android.view.ViewGroup;
25 import android.widget.AbsListView;
26 import android.widget.BaseExpandableListAdapter;
27 import android.widget.ExpandableListAdapter;
28 import android.widget.ExpandableListView;
29 import android.widget.ListView;
30 import android.widget.TextView;
33 * Utility base class for creating various Expandable List scenarios.
35 * WARNING: A lot of the features are mixed between ListView's expected position
36 * (flat list position) and an ExpandableListView's expected position. You must add/change
37 * features as you need them.
41 public abstract class ExpandableListScenario extends ListScenario {
42 protected ExpandableListAdapter mAdapter;
43 protected List<MyGroup> mGroups;
46 protected ListView createListView() {
47 return new ExpandableListView(this);
51 protected Params createParams() {
52 return new ExpandableParams();
56 protected void setAdapter(ListView listView) {
57 ((ExpandableListView) listView).setAdapter(mAdapter = createAdapter());
60 protected ExpandableListAdapter createAdapter() {
61 return new MyAdapter();
65 protected void readAndValidateParams(Params params) {
66 ExpandableParams expandableParams = (ExpandableParams) params;
68 int[] numChildren = expandableParams.mNumChildren;
70 mGroups = new ArrayList<MyGroup>(numChildren.length);
71 for (int i = 0; i < numChildren.length; i++) {
72 mGroups.add(new MyGroup(numChildren[i]));
75 expandableParams.superSetNumItems();
77 super.readAndValidateParams(params);
81 * Get the ExpandableListView widget.
82 * @return The main widget.
84 public ExpandableListView getExpandableListView() {
85 return (ExpandableListView) super.getListView();
88 public static class ExpandableParams extends Params {
89 private int[] mNumChildren;
92 * Sets the number of children per group.
94 * @param numChildrenPerGroup The number of children per group.
96 public ExpandableParams setNumChildren(int[] numChildren) {
97 mNumChildren = numChildren;
102 * Sets the number of items on the superclass based on the number of
103 * groups and children per group.
105 private ExpandableParams superSetNumItems() {
108 if (mNumChildren != null) {
109 for (int i = mNumChildren.length - 1; i >= 0; i--) {
110 numItems += mNumChildren[i];
114 super.setNumItems(numItems);
120 public Params setNumItems(int numItems) {
121 throw new IllegalStateException("Use setNumGroups and setNumChildren instead.");
125 public ExpandableParams setFadingEdgeScreenSizeFactor(double fadingEdgeScreenSizeFactor) {
126 return (ExpandableParams) super.setFadingEdgeScreenSizeFactor(fadingEdgeScreenSizeFactor);
130 public ExpandableParams setItemScreenSizeFactor(double itemScreenSizeFactor) {
131 return (ExpandableParams) super.setItemScreenSizeFactor(itemScreenSizeFactor);
135 public ExpandableParams setItemsFocusable(boolean itemsFocusable) {
136 return (ExpandableParams) super.setItemsFocusable(itemsFocusable);
140 public ExpandableParams setMustFillScreen(boolean fillScreen) {
141 return (ExpandableParams) super.setMustFillScreen(fillScreen);
145 public ExpandableParams setPositionScreenSizeFactorOverride(int position, double itemScreenSizeFactor) {
146 return (ExpandableParams) super.setPositionScreenSizeFactorOverride(position, itemScreenSizeFactor);
150 public ExpandableParams setPositionUnselectable(int position) {
151 return (ExpandableParams) super.setPositionUnselectable(position);
155 public ExpandableParams setStackFromBottom(boolean stackFromBottom) {
156 return (ExpandableParams) super.setStackFromBottom(stackFromBottom);
160 public ExpandableParams setStartingSelectionPosition(int startingSelectionPosition) {
161 return (ExpandableParams) super.setStartingSelectionPosition(startingSelectionPosition);
165 public ExpandableParams setConnectAdapter(boolean connectAdapter) {
166 return (ExpandableParams) super.setConnectAdapter(connectAdapter);
171 * Gets a string for the value of some item.
172 * @param packedPosition The position of the item.
173 * @return The string.
175 public final String getValueAtPosition(long packedPosition) {
176 final int type = ExpandableListView.getPackedPositionType(packedPosition);
178 if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
179 return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
180 .children.get(ExpandableListView.getPackedPositionChild(packedPosition))
182 } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
183 return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
186 throw new IllegalStateException("packedPosition is not a valid position.");
191 * Whether a particular position is out of bounds.
193 * @param packedPosition The packed position.
194 * @return Whether it's out of bounds.
196 private boolean isOutOfBounds(long packedPosition) {
197 final int type = ExpandableListView.getPackedPositionType(packedPosition);
199 if (type == ExpandableListView.PACKED_POSITION_TYPE_NULL) {
200 throw new IllegalStateException("packedPosition is not a valid position.");
203 final int group = ExpandableListView.getPackedPositionGroup(packedPosition);
204 if (group >= mGroups.size() || group < 0) {
208 if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
209 final int child = ExpandableListView.getPackedPositionChild(packedPosition);
210 if (child >= mGroups.get(group).children.size() || child < 0) {
219 * Gets a view for the packed position, possibly reusing the convertView.
221 * @param packedPosition The position to get a view for.
222 * @param convertView Optional view to convert.
223 * @param parent The future parent.
226 private View getView(long packedPosition, View convertView, ViewGroup parent) {
227 if (isOutOfBounds(packedPosition)) {
228 throw new IllegalStateException("position out of range for adapter!");
231 final ExpandableListView elv = getExpandableListView();
232 final int flPos = elv.getFlatListPosition(packedPosition);
234 if (convertView != null) {
235 ((TextView) convertView).setText(getValueAtPosition(packedPosition));
236 convertView.setId(flPos);
240 int desiredHeight = getHeightForPosition(flPos);
241 return createView(packedPosition, flPos, parent, desiredHeight);
245 * Create a view for a group or child position.
247 * @param packedPosition The packed position (has type, group pos, and optionally child pos).
248 * @param flPos The flat list position (the position that the ListView goes by).
249 * @param parent The parent view.
250 * @param desiredHeight The desired height.
253 protected View createView(long packedPosition, int flPos, ViewGroup parent, int desiredHeight) {
254 TextView result = new TextView(parent.getContext());
255 result.setHeight(desiredHeight);
256 result.setText(getValueAtPosition(packedPosition));
257 final ViewGroup.LayoutParams lp = new AbsListView.LayoutParams(
258 ViewGroup.LayoutParams.MATCH_PARENT,
259 ViewGroup.LayoutParams.WRAP_CONTENT);
260 result.setLayoutParams(lp);
261 result.setGravity(Gravity.CENTER_VERTICAL);
262 result.setPadding(36, 0, 0, 0);
268 * Returns a group index containing either the number of children or at
271 * @param numChildren The group must have this amount, or -1 if using
273 * @param atLeastOneChild The group must have at least one child, or false
274 * if using numChildren.
275 * @return A group index with the requirements.
277 public int findGroupWithNumChildren(int numChildren, boolean atLeastOneChild) {
278 final ExpandableListAdapter adapter = mAdapter;
280 for (int i = adapter.getGroupCount() - 1; i >= 0; i--) {
281 final int curNumChildren = adapter.getChildrenCount(i);
283 if (numChildren == curNumChildren || atLeastOneChild && curNumChildren > 0) {
291 public List<MyGroup> getGroups() {
295 public ExpandableListAdapter getAdapter() {
300 * Simple expandable list adapter.
302 protected class MyAdapter extends BaseExpandableListAdapter {
303 public Object getChild(int groupPosition, int childPosition) {
304 return getValueAtPosition(ExpandableListView.getPackedPositionForChild(groupPosition,
308 public long getChildId(int groupPosition, int childPosition) {
309 return mGroups.get(groupPosition).children.get(childPosition).id;
312 public int getChildrenCount(int groupPosition) {
313 return mGroups.get(groupPosition).children.size();
316 public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
317 View convertView, ViewGroup parent) {
318 return getView(ExpandableListView.getPackedPositionForChild(groupPosition,
319 childPosition), convertView, parent);
322 public Object getGroup(int groupPosition) {
323 return getValueAtPosition(ExpandableListView.getPackedPositionForGroup(groupPosition));
326 public int getGroupCount() {
327 return mGroups.size();
330 public long getGroupId(int groupPosition) {
331 return mGroups.get(groupPosition).id;
334 public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
336 return getView(ExpandableListView.getPackedPositionForGroup(groupPosition),
337 convertView, parent);
340 public boolean isChildSelectable(int groupPosition, int childPosition) {
344 public boolean hasStableIds() {
350 public static class MyGroup {
351 private static long mNextId = 1000;
355 List<MyChild> children;
357 public MyGroup(int numChildren) {
358 name = "Group " + id;
359 children = new ArrayList<MyChild>(numChildren);
360 for (int i = 0; i < numChildren; i++) {
361 children.add(new MyChild());
366 public static class MyChild {
367 private static long mNextId = 2000;
373 name = "Child " + id;
378 protected final void init(Params params) {
379 init((ExpandableParams) params);
383 * @see ListScenario#init
385 protected abstract void init(ExpandableParams params);