Home | History | Annotate | Download | only in util
      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 
     17 package android.util;
     18 
     19 import java.util.ArrayList;
     20 import java.util.List;
     21 
     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;
     31 
     32 /**
     33  * Utility base class for creating various Expandable List scenarios.
     34  * <p>
     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.
     38  *
     39  * @see ListScenario
     40  */
     41 public abstract class ExpandableListScenario extends ListScenario {
     42     protected ExpandableListAdapter mAdapter;
     43     protected List<MyGroup> mGroups;
     44 
     45     @Override
     46     protected ListView createListView() {
     47         return new ExpandableListView(this);
     48     }
     49 
     50     @Override
     51     protected Params createParams() {
     52         return new ExpandableParams();
     53     }
     54 
     55     @Override
     56     protected void setAdapter(ListView listView) {
     57         ((ExpandableListView) listView).setAdapter(mAdapter = createAdapter());
     58     }
     59 
     60     protected ExpandableListAdapter createAdapter() {
     61         return new MyAdapter();
     62     }
     63 
     64     @Override
     65     protected void readAndValidateParams(Params params) {
     66         ExpandableParams expandableParams = (ExpandableParams) params;
     67 
     68         int[] numChildren = expandableParams.mNumChildren;
     69 
     70         mGroups = new ArrayList<MyGroup>(numChildren.length);
     71         for (int i = 0; i < numChildren.length; i++) {
     72             mGroups.add(new MyGroup(numChildren[i]));
     73         }
     74 
     75         expandableParams.superSetNumItems();
     76 
     77         super.readAndValidateParams(params);
     78     }
     79 
     80     /**
     81      * Get the ExpandableListView widget.
     82      * @return The main widget.
     83      */
     84     public ExpandableListView getExpandableListView() {
     85         return (ExpandableListView) super.getListView();
     86     }
     87 
     88     public static class ExpandableParams extends Params {
     89         private int[] mNumChildren;
     90 
     91         /**
     92          * Sets the number of children per group.
     93          *
     94          * @param numChildrenPerGroup The number of children per group.
     95          */
     96         public ExpandableParams setNumChildren(int[] numChildren) {
     97             mNumChildren = numChildren;
     98             return this;
     99         }
    100 
    101         /**
    102          * Sets the number of items on the superclass based on the number of
    103          * groups and children per group.
    104          */
    105         private ExpandableParams superSetNumItems() {
    106             int numItems = 0;
    107 
    108             if (mNumChildren != null) {
    109                 for (int i = mNumChildren.length - 1; i >= 0; i--) {
    110                     numItems += mNumChildren[i];
    111                 }
    112             }
    113 
    114             super.setNumItems(numItems);
    115 
    116             return this;
    117         }
    118 
    119         @Override
    120         public Params setNumItems(int numItems) {
    121             throw new IllegalStateException("Use setNumGroups and setNumChildren instead.");
    122         }
    123 
    124         @Override
    125         public ExpandableParams setFadingEdgeScreenSizeFactor(double fadingEdgeScreenSizeFactor) {
    126             return (ExpandableParams) super.setFadingEdgeScreenSizeFactor(fadingEdgeScreenSizeFactor);
    127         }
    128 
    129         @Override
    130         public ExpandableParams setItemScreenSizeFactor(double itemScreenSizeFactor) {
    131             return (ExpandableParams) super.setItemScreenSizeFactor(itemScreenSizeFactor);
    132         }
    133 
    134         @Override
    135         public ExpandableParams setItemsFocusable(boolean itemsFocusable) {
    136             return (ExpandableParams) super.setItemsFocusable(itemsFocusable);
    137         }
    138 
    139         @Override
    140         public ExpandableParams setMustFillScreen(boolean fillScreen) {
    141             return (ExpandableParams) super.setMustFillScreen(fillScreen);
    142         }
    143 
    144         @Override
    145         public ExpandableParams setPositionScreenSizeFactorOverride(int position, double itemScreenSizeFactor) {
    146             return (ExpandableParams) super.setPositionScreenSizeFactorOverride(position, itemScreenSizeFactor);
    147         }
    148 
    149         @Override
    150         public ExpandableParams setPositionUnselectable(int position) {
    151             return (ExpandableParams) super.setPositionUnselectable(position);
    152         }
    153 
    154         @Override
    155         public ExpandableParams setStackFromBottom(boolean stackFromBottom) {
    156             return (ExpandableParams) super.setStackFromBottom(stackFromBottom);
    157         }
    158 
    159         @Override
    160         public ExpandableParams setStartingSelectionPosition(int startingSelectionPosition) {
    161             return (ExpandableParams) super.setStartingSelectionPosition(startingSelectionPosition);
    162         }
    163 
    164         @Override
    165         public ExpandableParams setConnectAdapter(boolean connectAdapter) {
    166             return (ExpandableParams) super.setConnectAdapter(connectAdapter);
    167         }
    168     }
    169 
    170     /**
    171      * Gets a string for the value of some item.
    172      * @param packedPosition The position of the item.
    173      * @return The string.
    174      */
    175     public final String getValueAtPosition(long packedPosition) {
    176         final int type = ExpandableListView.getPackedPositionType(packedPosition);
    177 
    178         if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
    179             return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
    180                     .children.get(ExpandableListView.getPackedPositionChild(packedPosition))
    181                     .name;
    182         } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
    183             return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
    184                     .name;
    185         } else {
    186             throw new IllegalStateException("packedPosition is not a valid position.");
    187         }
    188     }
    189 
    190     /**
    191      * Whether a particular position is out of bounds.
    192      *
    193      * @param packedPosition The packed position.
    194      * @return Whether it's out of bounds.
    195      */
    196     private boolean isOutOfBounds(long packedPosition) {
    197         final int type = ExpandableListView.getPackedPositionType(packedPosition);
    198 
    199         if (type == ExpandableListView.PACKED_POSITION_TYPE_NULL) {
    200             throw new IllegalStateException("packedPosition is not a valid position.");
    201         }
    202 
    203         final int group = ExpandableListView.getPackedPositionGroup(packedPosition);
    204         if (group >= mGroups.size() || group < 0) {
    205             return true;
    206         }
    207 
    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) {
    211                 return true;
    212             }
    213         }
    214 
    215         return false;
    216     }
    217 
    218     /**
    219      * Gets a view for the packed position, possibly reusing the convertView.
    220      *
    221      * @param packedPosition The position to get a view for.
    222      * @param convertView Optional view to convert.
    223      * @param parent The future parent.
    224      * @return A view.
    225      */
    226     private View getView(long packedPosition, View convertView, ViewGroup parent) {
    227         if (isOutOfBounds(packedPosition)) {
    228             throw new IllegalStateException("position out of range for adapter!");
    229         }
    230 
    231         final ExpandableListView elv = getExpandableListView();
    232         final int flPos = elv.getFlatListPosition(packedPosition);
    233 
    234         if (convertView != null) {
    235             ((TextView) convertView).setText(getValueAtPosition(packedPosition));
    236             convertView.setId(flPos);
    237             return convertView;
    238         }
    239 
    240         int desiredHeight = getHeightForPosition(flPos);
    241         return createView(packedPosition, flPos, parent, desiredHeight);
    242     }
    243 
    244     /**
    245      * Create a view for a group or child position.
    246      *
    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.
    251      * @return A view.
    252      */
    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);
    263         result.setId(flPos);
    264         return result;
    265     }
    266 
    267     /**
    268      * Returns a group index containing either the number of children or at
    269      * least one child.
    270      *
    271      * @param numChildren The group must have this amount, or -1 if using
    272      *            atLeastOneChild.
    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.
    276      */
    277     public int findGroupWithNumChildren(int numChildren, boolean atLeastOneChild) {
    278         final ExpandableListAdapter adapter = mAdapter;
    279 
    280         for (int i = adapter.getGroupCount() - 1; i >= 0; i--) {
    281             final int curNumChildren = adapter.getChildrenCount(i);
    282 
    283             if (numChildren == curNumChildren || atLeastOneChild && curNumChildren > 0) {
    284                 return i;
    285             }
    286         }
    287 
    288         return -1;
    289     }
    290 
    291     public List<MyGroup> getGroups() {
    292         return mGroups;
    293     }
    294 
    295     public ExpandableListAdapter getAdapter() {
    296         return mAdapter;
    297     }
    298 
    299     /**
    300      * Simple expandable list adapter.
    301      */
    302     protected class MyAdapter extends BaseExpandableListAdapter {
    303         public Object getChild(int groupPosition, int childPosition) {
    304             return getValueAtPosition(ExpandableListView.getPackedPositionForChild(groupPosition,
    305                     childPosition));
    306         }
    307 
    308         public long getChildId(int groupPosition, int childPosition) {
    309             return mGroups.get(groupPosition).children.get(childPosition).id;
    310         }
    311 
    312         public int getChildrenCount(int groupPosition) {
    313             return mGroups.get(groupPosition).children.size();
    314         }
    315 
    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);
    320         }
    321 
    322         public Object getGroup(int groupPosition) {
    323             return getValueAtPosition(ExpandableListView.getPackedPositionForGroup(groupPosition));
    324         }
    325 
    326         public int getGroupCount() {
    327             return mGroups.size();
    328         }
    329 
    330         public long getGroupId(int groupPosition) {
    331             return mGroups.get(groupPosition).id;
    332         }
    333 
    334         public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
    335                 ViewGroup parent) {
    336             return getView(ExpandableListView.getPackedPositionForGroup(groupPosition),
    337                     convertView, parent);
    338         }
    339 
    340         public boolean isChildSelectable(int groupPosition, int childPosition) {
    341             return true;
    342         }
    343 
    344         public boolean hasStableIds() {
    345             return true;
    346         }
    347 
    348     }
    349 
    350     public static class MyGroup {
    351         private static long mNextId = 1000;
    352 
    353         String name;
    354         long id = mNextId++;
    355         List<MyChild> children;
    356 
    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());
    362             }
    363         }
    364     }
    365 
    366     public static class MyChild {
    367         private static long mNextId = 2000;
    368 
    369         String name;
    370         long id = mNextId++;
    371 
    372         public MyChild() {
    373             name = "Child " + id;
    374         }
    375     }
    376 
    377     @Override
    378     protected final void init(Params params) {
    379         init((ExpandableParams) params);
    380     }
    381 
    382     /**
    383      * @see ListScenario#init
    384      */
    385     protected abstract void init(ExpandableParams params);
    386 }
    387