/*
* 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<Integer, Double> 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<Integer, Double> 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);
}
}
}