GridView 行重叠:如何使行高适合最高的项目? [英] GridView rows overlapping: how to make row height fit the tallest item?

查看:23
本文介绍了GridView 行重叠:如何使行高适合最高的项目?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

像 (不是权威副本,但是kernel.org 还是down),我们可以在fillDown() 和makeRow() 中看到最后看到的View 是参考视图":行的高度是从那个高度设置的查看,不是从最高的.这解释了为什么最右边的列没问题.不幸的是,GridView 没有很好地设置让我通过继承来解决这个问题.所有相关的字段和方法都是私有的.

所以,在我走上克隆并拥有"这个陈旧臃肿的道路之前,这里有什么我遗漏的技巧吗?我可以使用 TableLayout,但这需要我自己实现 numColumns="auto_fit"(因为我只想在手机屏幕上显示一个长列),而且它也不会是 AdapterView,这感觉应该是.>

事实上,clone 和 own 在这里并不实用.GridView 依赖于其父类和兄弟类中不可访问的部分,并且会导致导入至少 6000 行代码(AbsListView、AdapterView 等)

解决方案

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

编辑:我正确地完成了这项工作,但我在渲染之前预先测量了所有单元格.我通过继承 GridView 并在 onLayout 方法中添加一个测量挂钩来做到这一点.

/*** 共享一个最大高度的自定义视图组* @author 蔡斯科尔本*/公共类 GridViewItemLayout 扩展 LinearLayout {//每行的最大单元格高度数组私有静态 int[] mMaxRowHeight;//网格视图中的列数私有静态 int mNumColumns;//视图单元格的位置私人 int mPosition;//公共构造函数公共 GridViewItemLayout(上下文上下文){超级(上下文);}//公共构造函数公共 GridViewItemLayout(上下文上下文,AttributeSet attrs){超级(上下文,属性);}/*** 设置视图单元格的位置* @param 位置*/公共无效setPosition(int位置){mPosition = 位置;}/*** 设置列数和项目数以便准确存储* 每行的最大高度.每当布局发生变化时都必须调用它* 或内容数据.** @param numColumns* @param itemCount*/公共静态无效initItemLayout(int numColumns,int itemCount){mNumColumns = numColumns;mMaxRowHeight = new int[itemCount];}@覆盖protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//如果列数只有一,则不计算最大高度if(mNumColumns <= 1 || mMaxRowHeight == null) {返回;}//获取网格行的当前视图单元格索引int rowIndex = mPosition/mNumColumns;//获取此布局的测量高度int测量高度 = getMeasuredHeight();//如果当前高度大于之前的测量值,则更新数组if(measuredHeight > mMaxRowHeight[rowIndex]) {mMaxRowHeight[rowIndex] = 测量高度;}//更新布局的尺寸以反映最大高度setMeasuredDimension(getMeasuredWidth(), mMaxRowHeight[rowIndex]);}}

这是我的 BaseAdapter 子类中的测量函数.请注意,我有一个方法 updateItemDisplay 可以在视图单元格上设置所有适当的文本和图像.

/*** 遍历每个项目并强制测量以确定每行的最大高度*/public void measureItems(int columnWidth) {//获取系统充气机LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);//膨胀临时布局对象进行测量GridViewItemLayout itemView = (GridViewItemLayout)inflater.inflate(R.layout.list_confirm_item, null);//创建测量规范int widthMeasureSpec = MeasureSpec.makeMeasureSpec(columnWidth, MeasureSpec.EXACTLY);int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);//遍历每个数据对象for(int index = 0; index < mItems.size(); index++) {String[] item = mItems.get(index);//设置位置和数据itemView.setPosition(index);itemView.updateItemDisplay(item, mLanguage);//测力itemView.requestLayout();itemView.measure(widthMeasureSpec, heightMeasureSpec);}}

最后,这里是 GridView 子类,用于在布局期间测量视图单元格:

/*** 自定义网格视图子类来测量所有视图单元格* 以确定行的最大高度** @author 蔡斯科尔本*/公共类 AutoMeasureGridView 扩展 GridView {公共 AutoMeasureGridView(上下文上下文){超级(上下文);}公共 AutoMeasureGridView(上下文上下文,属性集属性){超级(上下文,属性);}公共 AutoMeasureGridView(上下文上下文,AttributeSet attrs,int defStyle){超级(上下文,属性,defStyle);}@覆盖protected void onLayout(boolean 改变,int l,int t,int r,int b) {如果(改变){CustomAdapter 适配器 = (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(改变, l, t, r, b);}}

我将列数作为资源的原因是,我可以根据方向等设置不同的列数.

Like this previous person, I have unwanted overlap between GridView items:

Notice the text, in every column except the rightmost one.

Where I differ from that previous question is that I don't want a constant row height. I want the row height to vary to accommodate the tallest content in each row, for efficient use of screen space.

Looking at the source for GridView (not the authoritative copy, but kernel.org is still down), we can see in fillDown() and makeRow() that the last View seen is the "reference view": the row's height is set from the height of that View, not from the tallest one. This explains why the rightmost column is ok. Unfortunately, GridView is not well set-up for me to fix this by inheritance. All the relevant fields and methods are private.

So, before I take the well-worn bloaty path of "clone and own", is there a trick I'm missing here? I could use a TableLayout, but that would require me to implement numColumns="auto_fit" myself (since I want e.g. just one long column on a phone screen), and it also wouldn't be an AdapterView, which this feels like it ought to be.

Edit: in fact, clone and own is not practical here. GridView depends on inaccessible parts of its parent and sibling classes, and would result in importing at least 6000 lines of code (AbsListView, AdapterView, etc.)

解决方案

I used a static array to drive max heights for the row. This is not perfect since the earlier columns will not be resized until the cell is redisplayed. Here is the code for the inflated reusable content view.

Edit: I got this work correctly but I had pre-measure all cells before rendering. I did this by subclassing GridView and adding a measuring hook in the onLayout method.

/**
 * 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]);
    }
}

Here is the measuring function in my BaseAdapter subclass. Note that I have a method updateItemDisplay that sets all appropriate text and images on the view cell.

    /**
     * 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);
        }
    }

And finally, here is the GridView subclass set up to measure view cells during layout:

/**
 * 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);
    }
}

The reason I have the number of columns as a resource is so that I can have a different number based on orientation, etc.

这篇关于GridView 行重叠:如何使行高适合最高的项目?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆