/*
* Copyright (C) 2012 Eyal LEZMY (http://www.eyal.fr)
*
* 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 fr.eyal.datalib.sample.netflix.ui;
import fr.eyal.datalib.sample.netflix.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* @author Eyal LEZMY
*
*/
public class GridLayout extends ViewGroup {
private int mNumRows;
private int mNumColumns;
private float mPreferredCellWidth;
private float mPreferredCellHeight;
private float mMarginColumn;
private float mMarginRow;
public static class LayoutParams extends android.view.ViewGroup.LayoutParams {
private int columnStart;
private int rowStart;
private int columnSpan;
private int rowSpan;
public LayoutParams(final Context c, final AttributeSet attrs) {
super(10, 10);
// get the layout attributes of it's child view from the xml layout file.
TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
columnStart = a.getInt(R.styleable.GridLayout_Layout_layout_column, 0);
rowStart = a.getInt(R.styleable.GridLayout_Layout_layout_row, 0);
columnSpan = a.getInt(R.styleable.GridLayout_Layout_layout_columnSpan, 1);
rowSpan = a.getInt(R.styleable.GridLayout_Layout_layout_rowSpan, 1);
a.recycle();
}
public LayoutParams(final int columnStart, final int rowStart, final int columnSpan, final int rowSpan) {
super(10, 10);
this.columnStart = columnStart;
this.rowStart = rowStart;
this.columnSpan = columnSpan;
this.rowSpan = rowSpan;
}
public int width() {
return columnSpan;
}
public int height() {
return rowSpan;
}
}
public GridLayout(final Context context) {
super(context);
}
public GridLayout(final Context context, final AttributeSet attrs) {
super(context, attrs);
readAttr(context, attrs);
}
private void readAttr(final Context c, final AttributeSet attrs) {
TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.GridLayout);
mNumRows = a.getInt(R.styleable.GridLayout_rows, 1);
mNumColumns = a.getInt(R.styleable.GridLayout_columns, 1);
mPreferredCellWidth = a.getDimension(R.styleable.GridLayout_preferredCellWidth, 1);
mPreferredCellHeight = a.getDimension(R.styleable.GridLayout_preferredCellHeight, 1);
mMarginColumn = a.getDimension(R.styleable.GridLayout_columnMargin, 0);
mMarginRow = a.getDimension(R.styleable.GridLayout_rowMargin, 0);
a.recycle();
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//we get the prefered bound sizes
float cellWidth = preferredCellWidth();
float cellHeight = preferredCellHeight();
//we compute the specific sizes
if (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST) {
float width = widthSize;
width = width - getPaddingLeft() - getPaddingRight();
width = width - mMarginColumn * (mNumColumns-1);
width /= mNumColumns;
if (widthMode == MeasureSpec.EXACTLY)
cellWidth = width;
else
cellWidth = Math.min(cellWidth, width);
}
if (heightMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.AT_MOST) {
float height = heightSize;
height = height - getPaddingTop() - getPaddingBottom();
height = height - mMarginRow * (mNumRows-1);
height /= mNumRows;
if (heightMode == MeasureSpec.EXACTLY)
cellHeight = height;
else
cellHeight = Math.min(cellHeight, height);
}
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int width = Math.round(cellWidth * lp.columnSpan + mMarginColumn * (lp.columnSpan-1));
int height = Math.round(cellHeight * lp.rowSpan + mMarginRow * (lp.rowSpan-1));
child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
}
int w = Math.round(cellWidth * mNumColumns + getPaddingLeft() + getPaddingRight() + mMarginColumn*(mNumColumns-1));
int h = Math.round(cellHeight * mNumRows + getPaddingTop() + getPaddingBottom() + mMarginRow*(mNumRows-1));
setMeasuredDimension(w, h);
}
@Override
protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
int width = r - l;
int height = b - t;
// if (lastWidth == width && lastHeight == height)
// return;
float cellWidth = width;
float cellHeight = height;
cellWidth = cellWidth - getPaddingLeft() - getPaddingRight();
cellWidth = cellWidth - (mNumColumns-1) * mMarginColumn;
cellHeight = cellHeight - getPaddingTop() - getPaddingBottom();
cellHeight = cellHeight - (mNumRows-1) * mMarginRow;
cellWidth /= mNumColumns;
cellHeight /= mNumRows;
int count = getChildCount();
// int posMin = mNumColumns;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child != null && child.getVisibility() != GONE) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int cl = Math.round(getPaddingLeft() + lp.columnStart * (cellWidth + mMarginColumn));
int ct = Math.round(getPaddingTop() + lp.rowStart * (cellHeight + mMarginRow));
int cr = Math.round(getPaddingLeft() + (lp.columnSpan + lp.columnStart) * cellWidth + (lp.columnStart + lp.columnSpan - 1) * mMarginColumn);
int cb = Math.round(getPaddingTop() + (lp.rowSpan + lp.rowStart) * cellHeight + (lp.rowStart + lp.rowSpan - 1) * mMarginRow);
child.layout(cl, ct, cr, cb);
}
}
}
public android.view.ViewGroup.LayoutParams generateLayoutParams(
final AttributeSet attrs) {
// let the GridLayout.LayoutParams to read attributes of layout instead of ViewGroup.LayoutParams
return new LayoutParams(getContext(), attrs);
}
protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p) {
// if the layout params is invalid, the android will throw a runtime exception.
if (p instanceof LayoutParams) {
int columns = columns(), rows = rows();
LayoutParams lp = (LayoutParams) p;
if ((lp.columnStart+lp.columnSpan) > columns || lp.columnStart < 0)
return false;
if ((lp.rowStart+lp.rowSpan) > rows || lp.rowStart < 0)
return false;
return true;
} else
return false;
}
public void setNumRows(final int mNumRows) {
this.mNumRows = mNumRows;
}
public void setNumColumns(final int nNumColumns) {
mNumColumns = nNumColumns;
}
public int rows() {
return mNumRows;
}
public int columns() {
return mNumColumns;
}
public float marginRow() {
return mMarginRow;
}
public float marginColumn() {
return mMarginColumn;
}
public float preferredCellWidth() {
return mPreferredCellWidth;
}
public float preferredCellHeight() {
return mPreferredCellHeight;
}
}