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 getGridView()62 public GridView getGridView() { 63 return mGridView; 64 } 65 getScreenHeight()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 */ getInitialNumItems()74 protected int getInitialNumItems() { 75 return mNumItems; 76 } 77 78 /** 79 * @return The desired height of 1 item, ignoring overrides 80 */ getDesiredItemHeight()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 */ setNumItems(int numItems)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 */ setStartingSelectionPosition(int startingSelectionPosition)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 */ setItemScreenSizeFactor(double itemScreenSizeFactor)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 */ setPositionScreenSizeFactorOverride( int position, double itemScreenSizeFactor)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 */ setStackFromBottom(boolean stackFromBottom)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 */ setMustFillScreen(boolean fillScreen)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 */ setColumnWidth(int requestedWidth)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 */ setNumColumns(int numColumns)176 public Params setNumColumns(int numColumns) { 177 mNumColumns = numColumns; 178 return this; 179 } 180 181 /** 182 * Sets the stretch mode. 183 */ setStretchMode(int stretchMode)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 */ setVerticalSpacing(int verticalSpacing)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 */ init(Params params)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 */ createAdapter()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" }) positionSelected(int positon)218 protected void positionSelected(int positon) { 219 220 } 221 222 /** 223 * Override this if you want to know that nothing is selected. 224 */ nothingSelected()225 protected void nothingSelected() { 226 227 } 228 229 @Override onCreate(Bundle icicle)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 */ readAndValidateParams(Params params)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 getValueAtPosition(int position)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 */ createView(int position, ViewGroup parent, int desiredHeight)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 { getCount()344 public int getCount() { 345 return mNumItems; 346 } 347 getItem(int position)348 public Object getItem(int position) { 349 return getValueAtPosition(position); 350 } 351 getItemId(int position)352 public long getItemId(int position) { 353 return position; 354 } 355 getView(int position, View convertView, ViewGroup parent)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