Я подумал, что это не должно быть слишком сложно при использовании пользовательского представления, что должно быть интересным упражнением. Это мой первый пользовательский вид; обратная связь приветствуется!
Ограничения
AspectGrid
полностью игнорирует размер, которым хотят быть его дети. Для ваших целей это выглядит нормально. Если нужно что-то более необычное, onMeasure
требуется много дополнительной работы.
- Размер, предложенный родителем
AspectGrid
, используется без раздумий. Это связано с предыдущей проблемой.
Скриншоты
Скриншот ландшафта http://i55.tinypic.com/fc8bj4.png
Портретный скриншот http://i55.tinypic.com/zn7qxk.png
Как это работает
Основным параметром является количество столбцов. Количество строк вычисляется автоматически, потому что мы знаем количество дочерних элементов. Другим основным параметром является соотношение сторон, которое мы хотим использовать для детей (для квадратов установлено значение 1).
В onLayout
мы получаем окончательный размер сетки, поэтому мы можем вычислить максимальную ширину и высоту дочерних элементов.
Затем мы сверяем это с соотношением сторон. Если дети слишком высокие, мы делаем их короче (как в портретном примере). Если они слишком широкие, мы делаем их уже (как в примере с ландшафтом).
Это все, что нужно сделать; остальное просто сантехника.
код
com/photogrid/AspectGrid.java
: фактический ViewGroup
класс
package com.photogrid;
import android.content.Context;
public class AspectGrid extends ViewGroup {
private int mNumColumns = 1;
private int mHorizontalSpacing = 0;
private int mVerticalSpacing = 0;
private float mChildAspectRatio = 1.0f;
public AspectGrid(Context context) {
super(context);
}
public AspectGrid(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AspectGrid(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
try {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AspectGrid);
setNumColumns(a.getInt(R.styleable.AspectGrid_numColumns, mNumColumns));
setHorizontalSpacing(a.getDimensionPixelSize(R.styleable.AspectGrid_horizontalSpacing, mHorizontalSpacing));
setVerticalSpacing(a.getDimensionPixelSize(R.styleable.AspectGrid_verticalSpacing, mVerticalSpacing));
setChildAspectRatio(a.getFloat(R.styleable.AspectGrid_childAspectRatio, mChildAspectRatio));
a.recycle();
} catch (RuntimeException ex) {
throw ex;
}
}
public int getNumColumns() {
return mNumColumns;
}
public void setNumColumns(int numColumns) {
if (numColumns < 1)
throw new IllegalArgumentException("numColumns must be at least 1");
if (numColumns != mNumColumns) {
mNumColumns = numColumns;
requestLayout();
}
}
public int getHorizontalSpacing() {
return mHorizontalSpacing;
}
public void setHorizontalSpacing(int horizontalSpacing) {
mHorizontalSpacing = horizontalSpacing;
}
public int getVerticalSpacing() {
return mVerticalSpacing;
}
public void setVerticalSpacing(int verticalSpacing) {
mVerticalSpacing = verticalSpacing;
}
public float getChildAspectRatio() {
return mChildAspectRatio;
}
public void setChildAspectRatio(float childAspectRatio) {
if (childAspectRatio <= 0)
throw new IllegalArgumentException("childAspectRatio must be positive");
if (childAspectRatio != mChildAspectRatio) {
mChildAspectRatio = childAspectRatio;
requestLayout();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int measuredWidth = widthSize;
int measuredHeight = heightSize;
int width = Math.max(measuredWidth, getSuggestedMinimumWidth());
int height = Math.max(measuredHeight, getSuggestedMinimumHeight());
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
if (childCount <= 0)
return;
int innerWidth = r - l - getPaddingLeft() - getPaddingRight();
int innerHeight = b - t - getPaddingBottom() - getPaddingTop();
int numRows = (childCount + mNumColumns - 1) / mNumColumns;
int leftEdge = getPaddingLeft();
int topEdge = getPaddingTop();
int horizontalStride = (innerWidth + mHorizontalSpacing) / mNumColumns;
int verticalStride = (innerHeight + mVerticalSpacing) / numRows;
int childWidth = horizontalStride - mHorizontalSpacing;
int childHeight = verticalStride - mVerticalSpacing;
if (childHeight * mChildAspectRatio > childWidth) {
childHeight = (int)(childWidth / mChildAspectRatio);
verticalStride = childHeight + mVerticalSpacing;
topEdge = (innerHeight + mVerticalSpacing - numRows * verticalStride) / 2;
} else {
childWidth = (int)(childHeight * mChildAspectRatio);
horizontalStride = childHeight + mHorizontalSpacing;
leftEdge = (innerWidth + mHorizontalSpacing - mNumColumns * horizontalStride) / 2;
}
for (int i = 0; i < childCount; ++i) {
View child = getChildAt(i);
int row = i / mNumColumns;
int column = i % mNumColumns;
int left = leftEdge + column * horizontalStride;
int top = topEdge + row * verticalStride;
child.layout(
left,
top,
left + childWidth,
top + childHeight);
}
}
}
res/values/attrs.xml
: объявление атрибутов для использования в XML
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AspectGrid">
<attr name="numColumns" format="integer"/>
<attr name="horizontalSpacing" format="dimension"/>
<attr name="verticalSpacing" format="dimension"/>
<attr name="childAspectRatio" format="float"/>
</declare-styleable>
</resources>
res/layout/main.xml
: пример, использованный на скриншотах выше
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.photogrid"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.photogrid.AspectGrid
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp"
app:numColumns="3"
app:horizontalSpacing="5dp"
app:verticalSpacing="5dp"
app:childAspectRatio="1.0"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffcccc"
android:text="Item 1"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ccffcc"
android:text="Item 2"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ccccff"
android:text="Item 3"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffffcc"
android:text="Item 4"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffccff"
android:text="Item 5"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ccffff"
android:text="Item 6"
/>
</com.photogrid.AspectGrid>
</LinearLayout>