/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.ListAdapter; import android.widget.TextView; import com.google.android.collect.Maps; import java.util.Map; /** * Utility base class for creating various GridView scenarios. Configurable by the number * of items, how tall each item should be (in relation to the screen height), and * what item should start with selection. */ public abstract class GridScenario extends Activity { private GridView mGridView; private int mNumItems; private int mStartingSelectionPosition; private double mItemScreenSizeFactor; private Map mOverrideItemScreenSizeFactors = Maps.newHashMap(); private int mScreenHeight; private boolean mStackFromBottom; private int mColumnWidth; private int mNumColumns; private int mStretchMode; private int mVerticalSpacing; public GridView getGridView() { return mGridView; } protected int getScreenHeight() { return mScreenHeight; } /** * @return The initial number of items in the grid as specified by the scenario. * This number may change over time. */ protected int getInitialNumItems() { return mNumItems; } /** * @return The desired height of 1 item, ignoring overrides */ public int getDesiredItemHeight() { return (int) (mScreenHeight * mItemScreenSizeFactor); } /** * Better way to pass in optional params than a honkin' paramater list :) */ public static class Params { private int mNumItems = 4; private int mStartingSelectionPosition = -1; private double mItemScreenSizeFactor = 1 / 5; private Map mOverrideItemScreenSizeFactors = Maps.newHashMap(); private boolean mStackFromBottom = false; private boolean mMustFillScreen = true; private int mColumnWidth = 0; private int mNumColumns = GridView.AUTO_FIT; private int mStretchMode = GridView.STRETCH_COLUMN_WIDTH; private int mVerticalSpacing = 0; /** * Set the number of items in the grid. */ public Params setNumItems(int numItems) { mNumItems = numItems; return this; } /** * Set the position that starts selected. * * @param startingSelectionPosition The selected position within the adapter's data set. * Pass -1 if you do not want to force a selection. * @return */ public Params setStartingSelectionPosition(int startingSelectionPosition) { mStartingSelectionPosition = startingSelectionPosition; return this; } /** * Set the factor that determines how tall each item is in relation to the * screen height. */ public Params setItemScreenSizeFactor(double itemScreenSizeFactor) { mItemScreenSizeFactor = itemScreenSizeFactor; return this; } /** * Override the item screen size factor for a particular item. Useful for * creating grids with non-uniform item height. * @param position The position in the grid. * @param itemScreenSizeFactor The screen size factor to use for the height. */ public Params setPositionScreenSizeFactorOverride( int position, double itemScreenSizeFactor) { mOverrideItemScreenSizeFactors.put(position, itemScreenSizeFactor); return this; } /** * Sets the stacking direction * @param stackFromBottom * @return */ public Params setStackFromBottom(boolean stackFromBottom) { mStackFromBottom = stackFromBottom; return this; } /** * Sets whether the sum of the height of the grid items must be at least the * height of the grid view. */ public Params setMustFillScreen(boolean fillScreen) { mMustFillScreen = fillScreen; return this; } /** * Sets the individual width of each column. * * @param requestedWidth the width in pixels of the column */ public Params setColumnWidth(int requestedWidth) { mColumnWidth = requestedWidth; return this; } /** * Sets the number of columns in the grid. */ public Params setNumColumns(int numColumns) { mNumColumns = numColumns; return this; } /** * Sets the stretch mode. */ public Params setStretchMode(int stretchMode) { mStretchMode = stretchMode; return this; } /** * Sets the spacing between rows in the grid */ public Params setVerticalSpacing(int verticalSpacing) { mVerticalSpacing = verticalSpacing; return this; } } /** * How each scenario customizes its behavior. * @param params */ protected abstract void init(Params params); /** * Override this to provide an different adapter for your scenario * @return The adapter that this scenario will use */ protected ListAdapter createAdapter() { return new MyAdapter(); } /** * Override this if you want to know when something has been selected (perhaps * more importantly, that {@link android.widget.AdapterView.OnItemSelectedListener} has * been triggered). */ @SuppressWarnings({ "UnusedDeclaration" }) protected void positionSelected(int positon) { } /** * Override this if you want to know that nothing is selected. */ protected void nothingSelected() { } @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); // turn off title bar requestWindowFeature(Window.FEATURE_NO_TITLE); mScreenHeight = getWindowManager().getDefaultDisplay().getHeight(); final Params params = new Params(); init(params); readAndValidateParams(params); mGridView = new GridView(this); mGridView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mGridView.setDrawSelectorOnTop(false); if (mNumColumns >= GridView.AUTO_FIT) { mGridView.setNumColumns(mNumColumns); } if (mColumnWidth > 0) { mGridView.setColumnWidth(mColumnWidth); } if (mVerticalSpacing > 0) { mGridView.setVerticalSpacing(mVerticalSpacing); } mGridView.setStretchMode(mStretchMode); mGridView.setAdapter(createAdapter()); if (mStartingSelectionPosition >= 0) { mGridView.setSelection(mStartingSelectionPosition); } mGridView.setPadding(10, 10, 10, 10); mGridView.setStackFromBottom(mStackFromBottom); mGridView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { public void onItemSelected(AdapterView parent, View v, int position, long id) { positionSelected(position); } public void onNothingSelected(AdapterView parent) { nothingSelected(); } }); setContentView(mGridView); } /** * Read in and validate all of the params passed in by the scenario. * @param params */ private void readAndValidateParams(Params params) { if (params.mMustFillScreen ) { double totalFactor = 0.0; for (int i = 0; i < params.mNumItems; i++) { if (params.mOverrideItemScreenSizeFactors.containsKey(i)) { totalFactor += params.mOverrideItemScreenSizeFactors.get(i); } else { totalFactor += params.mItemScreenSizeFactor; } } if (totalFactor < 1.0) { throw new IllegalArgumentException("grid items must combine to be at least " + "the height of the screen. this is not the case with " + params.mNumItems + " items and " + params.mItemScreenSizeFactor + " screen factor and " + "screen height of " + mScreenHeight); } } mNumItems = params.mNumItems; mStartingSelectionPosition = params.mStartingSelectionPosition; mItemScreenSizeFactor = params.mItemScreenSizeFactor; mOverrideItemScreenSizeFactors.putAll(params.mOverrideItemScreenSizeFactors); mStackFromBottom = params.mStackFromBottom; mColumnWidth = params.mColumnWidth; mNumColumns = params.mNumColumns; mStretchMode = params.mStretchMode; mVerticalSpacing = params.mVerticalSpacing; } public final String getValueAtPosition(int position) { return "postion " + position; } /** * Create a view for a grid item. Override this to create a custom view beyond * the simple focusable / unfocusable text view. * @param position The position. * @param parent The parent * @param desiredHeight The height the view should be to respect the desired item * to screen height ratio. * @return a view for the grid. */ protected View createView(int position, ViewGroup parent, int desiredHeight) { TextView result = new TextView(parent.getContext()); result.setHeight(desiredHeight); result.setText(getValueAtPosition(position)); final ViewGroup.LayoutParams lp = new AbsListView.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); result.setLayoutParams(lp); result.setId(position); result.setBackgroundColor(0x55ffffff); return result; } private class MyAdapter extends BaseAdapter { public int getCount() { return mNumItems; } public Object getItem(int position) { return getValueAtPosition(position); } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { if (convertView != null) { ((TextView) convertView).setText(getValueAtPosition(position)); convertView.setId(position); return convertView; } int desiredHeight = getDesiredItemHeight(); if (mOverrideItemScreenSizeFactors.containsKey(position)) { desiredHeight = (int) (mScreenHeight * mOverrideItemScreenSizeFactors.get(position)); } return createView(position, parent, desiredHeight); } } }