Как расположить «сетку» изображений в центре экрана - PullRequest
6 голосов
/ 04 января 2011

У меня есть набор из 12 изображений для отображения в макете. Число выбирается полу произвольно, но сводится к 3 по горизонтали и 4 по вертикали для портретного режима и наоборот для пейзажного.

Первая реализация использовала gridview. Проблема в том, что высота не может быть помещена на экран. В качестве обходного пути я могу (попытаться) масштабировать изображения, конечно, но практически невозможно рассчитать комнату, доступную для вида сетки: общий размер экрана известен, но вы должны «угадать» размер панели уведомлений, а это не так. Кажется, это элегантное решение. Измеренный размер на самом деле не заслуживает доверия: я не делаю полный перезапуск при изменении ориентации (для скорости), но способ построения экрана - полное пространство не доступно на месте. В итоге я пришел к выводу, что я не хочу рассчитывать размер изображений и затем масштабировать их соответствующим образом: я думаю, что лучше сказать, как представления должны соответствовать экрану, верно?

Итак, следующая попытка - просто использование TableLayout. Используя "ShrinkColumns =" ​​* "изображения подгоняются нормально, поэтому размер изображений теперь соответствует нашим размерам. Но« дополнительная »комната, которую мы можем иметь на высоте, теперь равномерно распределена между скринами. Этого следует ожидать, но некрасиво.

Текущий код кажется неактуальным, потому что он не работает, но в итоге выглядит так: Я удалил все отступы и другие вещи, которые не имеют отношения к делу. (для портрета :)

<TableLayout
       android:shrinkColumns="*">
    <TableRow>
        <ImageView/>
        <ImageView/>
        <ImageView/>
    </TableRow>
    … (repeat 3 tablerows)
</TableLayout>

Чтобы «масштабировать» слишком большие изображения, TableLayout имеет атрибут «shrinkcolumns =» * ».

Как мы можем добиться того, чтобы три ImageView были выровнены в центре TableRow и не были равномерно распределены по ширине? И то же самое касается вертикальных столбцов, как мы можем держать все вместе и не распределяться по высоте экрана? По сути, «лишнее» пространство должно идти в стороны как отступ / отступ, и теперь оно находится между изображениями.

Пример: Снимок экрана слева показывает слишком большое расстояние влево / вправо, справа слишком много верха / низа example-androblip-layout-image

1 Ответ

25 голосов
/ 06 января 2011

Я подумал, что это не должно быть слишком сложно при использовании пользовательского представления, что должно быть интересным упражнением. Это мой первый пользовательский вид; обратная связь приветствуется!

Ограничения

  • 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>
...