Подтвердить что ты не робот

Сложность строк GridView: как сделать высоту строки подходящей для самого высокого элемента?

Как этот предыдущий человек, у меня есть нежелательное совпадение между элементами GridView:

GridView items overlapping

Обратите внимание на текст в каждом столбце, кроме самого правого.

В отличие от предыдущего вопроса, я не хочу постоянной высоты строки. Я хочу, чтобы высота строки изменялась до в соответствии с самым высоким содержанием в каждой строке, для эффективного использования пространства экрана.

Глядя на источник для GridView (а не авторитетная копия, но kernel.org все еще недоступна), мы можем видеть в fillDown ( ) и makeRow(), что последний вид рассматривается как "ссылочный вид": высота строки задается с высоты этого представления, а не из самой высокой. Это объясняет, почему самый правый столбец в порядке. К сожалению, GridView не очень хорошо настроен для исправления этого путем наследования. Все соответствующие поля и методы являются закрытыми.

Итак, прежде чем я возьму изрядно раздутый путь "клона и собственного", есть трюк, который мне здесь не хватает? Я мог бы использовать TableLayout, но это потребует от меня Внесите numColumns="auto_fit" сам (так как я хочу, например, только один длинный столбец на экране телефона), и это также не будет AdaptViewView, который кажется, что это должно быть.

Изменить: на самом деле, клон и собственный не практичны. GridView зависит от недоступных частей его родительских и родственных классов и приведет к импорту не менее 6000 строк кода (AbsListView, AdapterView и т.д.).

4b9b3361

Ответ 1

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

Изменить. Я получил эту работу правильно, но перед рендерингом я предварительно измерил все ячейки. Я сделал это путем подклассификации GridView и добавления измерительного крючка в методе onLayout.

/**
 * Custom view group that shares a common max height
 * @author Chase Colburn
 */
public class GridViewItemLayout extends LinearLayout {

    // Array of max cell heights for each row
    private static int[] mMaxRowHeight;

    // The number of columns in the grid view
    private static int mNumColumns;

    // The position of the view cell
    private int mPosition;

    // Public constructor
    public GridViewItemLayout(Context context) {
        super(context);
    }

    // Public constructor
    public GridViewItemLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * Set the position of the view cell
     * @param position
     */
    public void setPosition(int position) {
        mPosition = position;
    }

    /**
     * Set the number of columns and item count in order to accurately store the
     * max height for each row. This must be called whenever there is a change to the layout
     * or content data.
     * 
     * @param numColumns
     * @param itemCount
     */
    public static void initItemLayout(int numColumns, int itemCount) {
        mNumColumns = numColumns;
        mMaxRowHeight = new int[itemCount];
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // Do not calculate max height if column count is only one
        if(mNumColumns <= 1 || mMaxRowHeight == null) {
            return;
        }

        // Get the current view cell index for the grid row
        int rowIndex = mPosition / mNumColumns;
        // Get the measured height for this layout
        int measuredHeight = getMeasuredHeight();
        // If the current height is larger than previous measurements, update the array
        if(measuredHeight > mMaxRowHeight[rowIndex]) {
            mMaxRowHeight[rowIndex] = measuredHeight;
        }
        // Update the dimensions of the layout to reflect the max height
        setMeasuredDimension(getMeasuredWidth(), mMaxRowHeight[rowIndex]);
    }
}

Вот измерительная функция в моем подклассе BaseAdapter. Обратите внимание, что у меня есть метод updateItemDisplay, который устанавливает все соответствующие текст и изображения в ячейке вида.

    /**
     * Run a pass through each item and force a measure to determine the max height for each row
     */
    public void measureItems(int columnWidth) {
        // Obtain system inflater
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        // Inflate temp layout object for measuring
        GridViewItemLayout itemView = (GridViewItemLayout)inflater.inflate(R.layout.list_confirm_item, null);

        // Create measuring specs
        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(columnWidth, MeasureSpec.EXACTLY);
        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);

        // Loop through each data object
        for(int index = 0; index < mItems.size(); index++) {
            String[] item = mItems.get(index);

            // Set position and data
            itemView.setPosition(index);
            itemView.updateItemDisplay(item, mLanguage);

            // Force measuring
            itemView.requestLayout();
            itemView.measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

И, наконец, вот подкласс GridView, настроенный для измерения ячеек просмотра во время компоновки:

/**
 * Custom subclass of grid view to measure all view cells
 * in order to determine the max height of the row
 * 
 * @author Chase Colburn
 */
public class AutoMeasureGridView extends GridView {

    public AutoMeasureGridView(Context context) {
        super(context);
    }

    public AutoMeasureGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public AutoMeasureGridView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(changed) {
            CustomAdapter adapter = (CustomAdapter)getAdapter();

            int numColumns = getContext().getResources().getInteger(R.integer.list_num_columns);
            GridViewItemLayout.initItemLayout(numColumns, adapter.getCount());

            if(numColumns > 1) {
                int columnWidth = getMeasuredWidth() / numColumns;
                adapter.measureItems(columnWidth);
            }
        }
        super.onLayout(changed, l, t, r, b);
    }
}

Причина, по которой у меня есть количество столбцов в качестве ресурса, заключается в том, что у меня может быть другое число, основанное на ориентации и т.д.

Ответ 2

Основываясь на информации от Криса, я использовал это обходное решение, используя ссылочный вид, используемый родным GridView при определении высоты других элементов GridView.

Я создал этот настраиваемый класс GridViewItemContainer:

/**
 * This class makes sure that all items in a GridView row are of the same height.
 * (Could extend FrameLayout, LinearLayout etc as well, RelativeLayout was just my choice here)
 * @author Anton Spaans
 *
*/
public class GridViewItemContainer extends RelativeLayout {
private View[] viewsInRow;

public GridViewItemContainer(Context context) {
    super(context);
}

public GridViewItemContainer(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

public GridViewItemContainer(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public void setViewsInRow(View[] viewsInRow) {
    if  (viewsInRow != null) {
        if (this.viewsInRow == null) {
            this.viewsInRow = Arrays.copyOf(viewsInRow, viewsInRow.length);
        }
        else {
            System.arraycopy(viewsInRow, 0, this.viewsInRow, 0, viewsInRow.length);
        }
    }
    else if (this.viewsInRow != null){
        Arrays.fill(this.viewsInRow, null);
    }
}

@Override
protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (viewsInRow == null) {
        return;
    }

    int measuredHeight = getMeasuredHeight();
    int maxHeight      = measuredHeight;
    for (View siblingInRow : viewsInRow) {
        if  (siblingInRow != null) {
            maxHeight = Math.max(maxHeight, siblingInRow.getMeasuredHeight());
        }
    }

    if (maxHeight == measuredHeight) {
        return;
    }

    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    switch(heightMode) {
    case MeasureSpec.AT_MOST:
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(maxHeight, heightSize), MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        break;

    case MeasureSpec.EXACTLY:
        // No debate here. Final measuring already took place. That it.
        break;

    case MeasureSpec.UNSPECIFIED:
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        break;

    }
}

В вашем методе getView адаптера либо оберните преобразование в качестве дочернего элемента в новом GridViewItemContainer, либо сделайте его одним из лучших элементов XML вашего макета:

        // convertView has been just been inflated or came from getView parameter.
        if (!(convertView instanceof GridViewItemContainer)) {
            ViewGroup container = new GridViewItemContainer(inflater.getContext());

            // If you have tags, move them to the new top element. E.g.:
            container.setTag(convertView.getTag());
            convertView.setTag(null);

            container.addView(convertView);
            convertView = container;
        }
        ...
        ...
        viewsInRow[position % numColumns] = convertView;
        GridViewItemContainer referenceView = (GridViewItemContainer)convertView;
        if ((position % numColumns == (numColumns-1)) || (position == getCount()-1)) {
            referenceView.setViewsInRow(viewsInRow);
        }
        else {
            referenceView.setViewsInRow(null);
        }

Где numColumns - количество столбцов в GridView, а "viewsInRow" - это список View в текущей строке, где находится "позиция".

Ответ 3

Я провел очень много исследований, но нашел неполный ответ или испытывал затруднения с пониманием того, что происходит с решением, но в конце концов нашел ответ, который идеально подходил для правильного объяснения.

Моя проблема состояла в том, чтобы правильно подогнать элемент gridview по высоте. Этот вид Grid-view отлично работал, когда все ваши виды имеют одинаковую высоту. Но когда ваши представления имеют разную высоту, сетка не ведет себя так, как ожидалось. Виды будут перекрывать друг друга, создавая an-aesthetically приятную сетку.

Здесь Решение Я использовал этот класс в формате XML.

Я использовал это решение, и оно работает очень хорошо, большое спасибо. --Abhishek Mittal

Ответ 4

Если вы преобразуете свой GridView или ListView в RecyclerView, эта проблема не произойдет. И вам не нужно будет создавать собственный класс GridView.

Ответ 5

Давление веса на ваш GridView также работает на GridViews внутри LinearLayouts в качестве ребенка. Таким образом, GridView заполняет окно просмотра своими дочерними элементами, поэтому вы можете просматривать его, если они соответствуют экрану (тогда вы прокручиваете).

Но всегда избегайте использования GridViews внутри ScrollViews. В противном случае вам нужно будет рассчитать каждую высоту ребенка и переназначить их, как сказал Chase выше.

<GridView
    android:id="@+id/gvFriends"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:verticalSpacing="5dp"
    android:horizontalSpacing="5dp"
    android:clipChildren="false"
    android:listSelector="@android:color/transparent"
    android:scrollbarAlwaysDrawHorizontalTrack="false"
    android:scrollbarAlwaysDrawVerticalTrack="false"
    android:stretchMode="columnWidth"
    android:scrollbars="none"
    android:numColumns="4"/>