GridView行重叠:如何使行高适合最高的项目?

Chr*_*yle 43 android android-layout android-gridview

以前的人一样,我在GridView项目之间有不必要的重叠:

GridView项重叠

请注意除最右边的列之外的每个列中的文本.

我与前一个问题的不同之处在于我不想要一个恒定的行高.我希望行高可以变化以适应每行中最高的内容,以有效利用屏幕空间.

查看GridView源代码(不是权威副本,但kernel.org仍然是down),我们可以在fillDown()和makeRow()中看到最后一个View看到的是"引用视图":行的高度是从视图的高度,而不是最高的视图.这解释了为什么最右边的列没问题.不幸的是,GridView没有很好的设置让我通过继承来解决这个问题.所有相关领域和方法都是私人的.

所以,在我采取"克隆和拥有"这条破旧的臃肿路径之前,我在这里缺少一个技巧吗?我可以使用TableLayout,但这需要我自己实现numColumns="auto_fit"(因为我想在电话屏幕上只需要一个长列),它也不会是一个AdapterView,这应该是它应该是.

编辑:事实上,克隆和自己在这里不实用.GridView依赖于其父类和兄弟类的不可访问部分,并导致至少导入6000行代码(AbsListView,AdapterView等)

Cha*_*ase 27

我使用静态数组来驱动行的最大高度.这并不完美,因为在重新显示单元格之前,不会调整早期列的大小.这是膨胀的可重用内容视图的代码.

编辑:我正确地完成了这项工作,但我在渲染之前预先测量了所有单元格.我通过继承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]);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

最后,这里是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我将列数作为资源的原因是我可以根据方向等具有不同的数字.

  • 我喜欢你的想法,但在测试或使用之前我有很多疑问.首先,您将所有行高存储在静态变量中.我认为在同一个View中使用多个GridView会很麻烦.第二,如果'numColumns'是'auto_fit'怎么办?第三,在'AutoMeasureGridView'的'onLayout'内测量'columnWidth'时.您正在使用'getMeasuredWidth()/ numColumns'.但水平间距怎么样?不应该是'getMeasuredWidth()/(numColumns +((numColumns-1)*horizo​​ntalSpacing))'?谢谢 :) (4认同)
  • 请你上传你的 itemView.updateItemDisplay(item, mLanguage); 方法,让我困惑,这个答案对我有帮助,但我对 updateItemDisplay() 方法感到困惑 (2认同)