package com.antipodalwall;
import com.actionbarsherlock.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.provider.SyncStateContract.Columns;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@SuppressWarnings("unused")
public class AntipodalWallLayout extends ViewGroup {
/**
* The number of columns to distribute views as stablished with the
* android:columnCount property in XML
*/
private int mColumns;
/**
* The width of a column after calculating and dividing the usable width
* between columns
*/
private float mColumnWidth = 0;
/** Left padding as stablished with android:paddingLeft in the XML layout */
private int mPaddingL;
/** Top padding as stablished with android:paddingTop in the XML layout */
private int mPaddingT;
/** Right padding as stablished with android:paddingRight in the XML layout */
private int mPaddingR;
/**
* Bottom padding as stablished with android:paddingBottom in the XML layout
*/
// TODO Not used right now, but may be useful in the future
private int mPaddingB;
/** The total visible height assigned to the layout */
int mLayoutHeight = 0;
/**
* The final total height of the layout, visible or not, after adding
* children. All content scrolled or not.
*/
private int mFinalHeight = 0;
/**
* Horizontal space between chldren as stablished with
* android:horizontalSpacing in the XML layout
*/
private int mHorizontalSpacing;
/**
* Horizontal space between chldren as stablished with
* android:verticalSpacing in the XML layout
*/
private int mVerticalSpacing;
public AntipodalWallLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
// Load the attrs from the stablished in the XML layout, if any
final TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.AntipodalWallAttrs);
// - scrollbars
initializeScrollbars(a);
// - number of columns
mColumns = a.getInt(R.styleable.AntipodalWallAttrs_android_columnCount,
1);
if (mColumns < 1)
mColumns = 1; // the default is one column
// - general padding (padding was not being handled correctly)
setGeneralPadding(a.getDimensionPixelSize(
R.styleable.AntipodalWallAttrs_android_padding, 0));
// - specific paddings
mPaddingL = a.getDimensionPixelSize(
R.styleable.AntipodalWallAttrs_android_paddingLeft, 0);
mPaddingT = a.getDimensionPixelSize(
R.styleable.AntipodalWallAttrs_android_paddingTop, 0);
mPaddingR = a.getDimensionPixelSize(
R.styleable.AntipodalWallAttrs_android_paddingRight, 0);
mPaddingB = a.getDimensionPixelSize(
R.styleable.AntipodalWallAttrs_android_paddingBottom, 0);
// - spacing
mHorizontalSpacing = a.getDimensionPixelSize(
R.styleable.AntipodalWallAttrs_android_horizontalSpacing, 0);
mVerticalSpacing = a.getDimensionPixelSize(
R.styleable.AntipodalWallAttrs_android_verticalSpacing, 0);
awakenScrollBars(); // TODO Scrollbars should be shown only if enabled
// in XML attributes
}
/*
* (non-Javadoc)
*
* @see android.view.View#onMeasure(int, int)
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Total layout width
int layoutWidth = MeasureSpec.getSize(widthMeasureSpec);
// Usable layout width for children once padding is removed
int layoutUsableWidth = layoutWidth - mPaddingL - mPaddingR;
if (layoutUsableWidth < 0)
layoutUsableWidth = 0;
// Total layout height
mLayoutHeight = MeasureSpec.getSize(heightMeasureSpec);
// Calculate width assigned to each column: the usable width divided by
// the number of columns, minus horizontal spacing
mColumnWidth = layoutUsableWidth / mColumns
- ((mHorizontalSpacing * (mColumns - 1)) / mColumns);
// Measure each children
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
// force the width of the children to be the width previously
// calculated for columns...
int childWidthSpec = MeasureSpec.makeMeasureSpec(
(int) mColumnWidth, MeasureSpec.EXACTLY);
// ... but let them grow vertically
int childHeightSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
child.measure(childWidthSpec, childHeightSpec);
}
// Get the final total height of the layout. It will be that of the
// higher column once all chidren are in place. Every child is added to
// the sortest column at the moment of addition
int[] columnsHeights = new int[mColumns];
for (int i = 0; i < getChildCount(); i++) {
int column = findColumn(columnsHeights, ColumnSpec.LOWEST);
columnsHeights[column] += getChildAt(i).getMeasuredHeight();
}
mFinalHeight = columnsHeights[findColumn(columnsHeights,
ColumnSpec.HIGHEST)];
setMeasuredDimension(layoutWidth, mFinalHeight);
}
/*
* (non-Javadoc)
*
* @see android.view.ViewGroup#onLayout(boolean, int, int, int, int)
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int[] columnsHeights = new int[mColumns];
// We place each child in the column that has the sortest height at the
// moment
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
int column = findColumn(columnsHeights, ColumnSpec.LOWEST);
int left = mPaddingL + l + (int) (mColumnWidth * column)
+ (mHorizontalSpacing * column);
view.layout(left, columnsHeights[column] + mPaddingT,
left + view.getMeasuredWidth(), columnsHeights[column]
+ view.getMeasuredHeight() + mPaddingT);
columnsHeights[column] = columnsHeights[column]
+ view.getMeasuredHeight() + mVerticalSpacing;
}
}
/*
* (non-Javadoc)
*
* @see android.view.View#computeVerticalScrollExtent()
*/
@Override
protected int computeVerticalScrollExtent() {
return mLayoutHeight - (mFinalHeight - mLayoutHeight);
}
/*
* (non-Javadoc)
*
* @see android.view.View#computeVerticalScrollOffset()
*/
@Override
protected int computeVerticalScrollOffset() {
return getScrollY();
}
/*
* (non-Javadoc)
*
* @see android.view.View#computeVerticalScrollRange()
*/
@Override
protected int computeVerticalScrollRange() {
return mFinalHeight;
}
/*
* (non-Javadoc)
*
* @see android.view.View#onTouchEvent(android.view.MotionEvent)
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
// Handle vertical scrolling
// TODO only do this if scrolling is enabled in XML
if (isVerticalScrollBarEnabled()) {
if (event.getHistorySize() > 0) {
int yMove = -(int) (event.getY() - event
.getHistoricalY(event.getHistorySize() - 1));
int result_scroll = getScrollY() + yMove;
if (result_scroll >= 0
&& result_scroll <= mFinalHeight - mLayoutHeight)
scrollBy(0, yMove);
}
}
break;
}
return true;
}
/**
* Finds a column according to the specification passed (right now only
* HIGHEST or LOWEST)
*
* @param columns
* the columns array
* @param which
* which column specification to find
* @return the found column index
*/
private int findColumn(int[] columns, int which) {
int comp = columns[0];
int column = 0;
for (int i = 1; i < columns.length; i++) {
boolean replace = false;
switch (which) {
case ColumnSpec.LOWEST:
replace = columns[i] < comp;
break;
case ColumnSpec.HIGHEST:
replace = columns[i] > comp;
break;
}
if (replace) {
comp = columns[i];
column = i;
}
}
return column;
}
private void setGeneralPadding(int padding) {
mPaddingL = padding;
mPaddingT = padding;
mPaddingR = padding;
mPaddingB = padding;
}
}