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 android.app.Activity;
     20 import android.os.Bundle;
     21 import android.view.View;
     22 import android.view.ViewGroup;
     23 import android.view.Window;
     24 import android.widget.AbsListView;
     25 import android.widget.AdapterView;
     26 import android.widget.BaseAdapter;
     27 import android.widget.GridView;
     28 import android.widget.ListAdapter;
     29 import android.widget.TextView;
     30 
     31 import com.google.android.collect.Maps;
     32 
     33 import java.util.Map;
     34 
     35 /**
     36  * Utility base class for creating various GridView scenarios.  Configurable by the number
     37  * of items, how tall each item should be (in relation to the screen height), and
     38  * what item should start with selection.
     39  */
     40 public abstract class GridScenario extends Activity {
     41 
     42     private GridView mGridView;
     43 
     44     private int mNumItems;
     45 
     46     private int mStartingSelectionPosition;
     47     private double mItemScreenSizeFactor;
     48     private Map<Integer, Double> mOverrideItemScreenSizeFactors = Maps.newHashMap();
     49 
     50     private int mScreenHeight;
     51 
     52     private boolean mStackFromBottom;
     53 
     54     private int mColumnWidth;
     55 
     56     private int mNumColumns;
     57 
     58     private int mStretchMode;
     59 
     60     private int mVerticalSpacing;
     61 
     62     public GridView getGridView() {
     63         return mGridView;
     64     }
     65 
     66     protected int getScreenHeight() {
     67         return mScreenHeight;
     68     }
     69 
     70     /**
     71      * @return The initial number of items in the grid as specified by the scenario.
     72      * This number may change over time.
     73      */
     74     protected int getInitialNumItems() {
     75         return mNumItems;
     76     }
     77 
     78     /**
     79      * @return The desired height of 1 item, ignoring overrides
     80      */
     81     public int getDesiredItemHeight() {
     82         return (int) (mScreenHeight * mItemScreenSizeFactor);
     83     }
     84 
     85     /**
     86      * Better way to pass in optional params than a honkin' paramater list :)
     87      */
     88     public static class Params {
     89         private int mNumItems = 4;
     90         private int mStartingSelectionPosition = -1;
     91         private double mItemScreenSizeFactor = 1 / 5;
     92 
     93         private Map<Integer, Double> mOverrideItemScreenSizeFactors = Maps.newHashMap();
     94 
     95         private boolean mStackFromBottom = false;
     96         private boolean mMustFillScreen = true;
     97 
     98         private int mColumnWidth = 0;
     99         private int mNumColumns = GridView.AUTO_FIT;
    100         private int mStretchMode = GridView.STRETCH_COLUMN_WIDTH;
    101         private int mVerticalSpacing = 0;
    102 
    103         /**
    104          * Set the number of items in the grid.
    105          */
    106         public Params setNumItems(int numItems) {
    107             mNumItems = numItems;
    108             return this;
    109         }
    110 
    111         /**
    112          * Set the position that starts selected.
    113          *
    114          * @param startingSelectionPosition The selected position within the adapter's data set.
    115          * Pass -1 if you do not want to force a selection.
    116          * @return
    117          */
    118         public Params setStartingSelectionPosition(int startingSelectionPosition) {
    119             mStartingSelectionPosition = startingSelectionPosition;
    120             return this;
    121         }
    122 
    123         /**
    124          * Set the factor that determines how tall each item is in relation to the
    125          * screen height.
    126          */
    127         public Params setItemScreenSizeFactor(double itemScreenSizeFactor) {
    128             mItemScreenSizeFactor = itemScreenSizeFactor;
    129             return this;
    130         }
    131 
    132         /**
    133          * Override the item screen size factor for a particular item.  Useful for
    134          * creating grids with non-uniform item height.
    135          * @param position The position in the grid.
    136          * @param itemScreenSizeFactor The screen size factor to use for the height.
    137          */
    138         public Params setPositionScreenSizeFactorOverride(
    139                 int position, double itemScreenSizeFactor) {
    140             mOverrideItemScreenSizeFactors.put(position, itemScreenSizeFactor);
    141             return this;
    142         }
    143 
    144         /**
    145          * Sets the stacking direction
    146          * @param stackFromBottom
    147          * @return
    148          */
    149         public Params setStackFromBottom(boolean stackFromBottom) {
    150             mStackFromBottom = stackFromBottom;
    151             return this;
    152         }
    153 
    154         /**
    155          * Sets whether the sum of the height of the grid items must be at least the
    156          * height of the grid view.
    157          */
    158         public Params setMustFillScreen(boolean fillScreen) {
    159             mMustFillScreen = fillScreen;
    160             return this;
    161         }
    162 
    163         /**
    164          * Sets the individual width of each column.
    165          *
    166          * @param requestedWidth the width in pixels of the column
    167          */
    168         public Params setColumnWidth(int requestedWidth) {
    169             mColumnWidth = requestedWidth;
    170             return this;
    171         }
    172 
    173         /**
    174          * Sets the number of columns in the grid.
    175          */
    176         public Params setNumColumns(int numColumns) {
    177             mNumColumns = numColumns;
    178             return this;
    179         }
    180 
    181         /**
    182          * Sets the stretch mode.
    183          */
    184         public Params setStretchMode(int stretchMode) {
    185             mStretchMode = stretchMode;
    186             return this;
    187         }
    188 
    189         /**
    190          * Sets the spacing between rows in the grid
    191          */
    192         public Params setVerticalSpacing(int verticalSpacing) {
    193             mVerticalSpacing  = verticalSpacing;
    194             return this;
    195         }
    196     }
    197 
    198     /**
    199      * How each scenario customizes its behavior.
    200      * @param params
    201      */
    202     protected abstract void init(Params params);
    203 
    204     /**
    205      * Override this to provide an different adapter for your scenario
    206      * @return The adapter that this scenario will use
    207      */
    208     protected ListAdapter createAdapter() {
    209         return new MyAdapter();
    210     }
    211 
    212     /**
    213      * Override this if you want to know when something has been selected (perhaps
    214      * more importantly, that {@link android.widget.AdapterView.OnItemSelectedListener} has
    215      * been triggered).
    216      */
    217     @SuppressWarnings({ "UnusedDeclaration" })
    218     protected void positionSelected(int positon) {
    219 
    220     }
    221 
    222     /**
    223      * Override this if you want to know that nothing is selected.
    224      */
    225     protected void nothingSelected() {
    226 
    227     }
    228 
    229     @Override
    230     protected void onCreate(Bundle icicle) {
    231         super.onCreate(icicle);
    232 
    233         // turn off title bar
    234         requestWindowFeature(Window.FEATURE_NO_TITLE);
    235 
    236         mScreenHeight = getWindowManager().getDefaultDisplay().getHeight();
    237 
    238         final Params params = new Params();
    239         init(params);
    240 
    241         readAndValidateParams(params);
    242 
    243         mGridView = new GridView(this);
    244         mGridView.setLayoutParams(new ViewGroup.LayoutParams(
    245                 ViewGroup.LayoutParams.MATCH_PARENT,
    246                 ViewGroup.LayoutParams.MATCH_PARENT));
    247         mGridView.setDrawSelectorOnTop(false);
    248         if (mNumColumns >= GridView.AUTO_FIT) {
    249             mGridView.setNumColumns(mNumColumns);
    250         }
    251         if (mColumnWidth > 0) {
    252             mGridView.setColumnWidth(mColumnWidth);
    253         }
    254         if (mVerticalSpacing > 0) {
    255             mGridView.setVerticalSpacing(mVerticalSpacing);
    256         }
    257         mGridView.setStretchMode(mStretchMode);
    258         mGridView.setAdapter(createAdapter());
    259         if (mStartingSelectionPosition >= 0) {
    260             mGridView.setSelection(mStartingSelectionPosition);
    261         }
    262         mGridView.setPadding(10, 10, 10, 10);
    263         mGridView.setStackFromBottom(mStackFromBottom);
    264 
    265         mGridView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    266             public void onItemSelected(AdapterView parent, View v, int position, long id) {
    267                 positionSelected(position);
    268             }
    269 
    270             public void onNothingSelected(AdapterView parent) {
    271                 nothingSelected();
    272             }
    273         });
    274 
    275         setContentView(mGridView);
    276     }
    277 
    278 
    279 
    280     /**
    281      * Read in and validate all of the params passed in by the scenario.
    282      * @param params
    283      */
    284     private void readAndValidateParams(Params params) {
    285         if (params.mMustFillScreen ) {
    286             double totalFactor = 0.0;
    287             for (int i = 0; i < params.mNumItems; i++) {
    288                 if (params.mOverrideItemScreenSizeFactors.containsKey(i)) {
    289                     totalFactor += params.mOverrideItemScreenSizeFactors.get(i);
    290                 } else {
    291                     totalFactor += params.mItemScreenSizeFactor;
    292                 }
    293             }
    294             if (totalFactor < 1.0) {
    295                 throw new IllegalArgumentException("grid items must combine to be at least " +
    296                         "the height of the screen.  this is not the case with " + params.mNumItems
    297                         + " items and " + params.mItemScreenSizeFactor + " screen factor and " +
    298                         "screen height of " + mScreenHeight);
    299             }
    300         }
    301 
    302         mNumItems = params.mNumItems;
    303         mStartingSelectionPosition = params.mStartingSelectionPosition;
    304         mItemScreenSizeFactor = params.mItemScreenSizeFactor;
    305 
    306         mOverrideItemScreenSizeFactors.putAll(params.mOverrideItemScreenSizeFactors);
    307 
    308         mStackFromBottom = params.mStackFromBottom;
    309         mColumnWidth = params.mColumnWidth;
    310         mNumColumns = params.mNumColumns;
    311         mStretchMode = params.mStretchMode;
    312         mVerticalSpacing = params.mVerticalSpacing;
    313     }
    314 
    315     public final String getValueAtPosition(int position) {
    316         return "postion " + position;
    317     }
    318 
    319     /**
    320      * Create a view for a grid item.  Override this to create a custom view beyond
    321      * the simple focusable / unfocusable text view.
    322      * @param position The position.
    323      * @param parent The parent
    324      * @param desiredHeight The height the view should be to respect the desired item
    325      *   to screen height ratio.
    326      * @return a view for the grid.
    327      */
    328     protected View createView(int position, ViewGroup parent, int desiredHeight) {
    329         TextView result = new TextView(parent.getContext());
    330         result.setHeight(desiredHeight);
    331         result.setText(getValueAtPosition(position));
    332         final ViewGroup.LayoutParams lp = new AbsListView.LayoutParams(
    333                 ViewGroup.LayoutParams.MATCH_PARENT,
    334                 ViewGroup.LayoutParams.WRAP_CONTENT);
    335         result.setLayoutParams(lp);
    336         result.setId(position);
    337         result.setBackgroundColor(0x55ffffff);
    338         return result;
    339     }
    340 
    341 
    342 
    343     private class MyAdapter extends BaseAdapter {
    344         public int getCount() {
    345             return mNumItems;
    346         }
    347 
    348         public Object getItem(int position) {
    349             return getValueAtPosition(position);
    350         }
    351 
    352         public long getItemId(int position) {
    353             return position;
    354         }
    355 
    356         public View getView(int position, View convertView, ViewGroup parent) {
    357             if (convertView != null) {
    358                 ((TextView) convertView).setText(getValueAtPosition(position));
    359                 convertView.setId(position);
    360                 return convertView;
    361             }
    362 
    363             int desiredHeight = getDesiredItemHeight();
    364             if (mOverrideItemScreenSizeFactors.containsKey(position)) {
    365                 desiredHeight = (int) (mScreenHeight * mOverrideItemScreenSizeFactors.get(position));
    366             }
    367             return createView(position, parent, desiredHeight);
    368         }
    369     }
    370 }
    371