ListView嵌套GridView使用解析

ListView嵌套GridView使用解析

前言

本文是ListView嵌套GridView的结构。

网上已经解决,不过还是自己做个记录,顺带的了解了解view的测量方案。

问题是嵌套后GridView只能显示一行,这里就涉及到view的高度计算了。

因为GridView是在ListView的条目中,所以他的高度是受到条目高度的控制,item的高度是受到ListView的控制。

解决的方案:

通过改变GridView的高度测量规则来显示完整的GridView。

方案1.

直接改变测量规则,真正的测量工还是交给GridView的默认测量方法。

方案2.

我们手动的去测量每个Item的高度,然后做累加。

上述方案要注意的是,不适合数据量较大的列表,因为上述方案会导致条目无法复用,一次性都加载出来。

看一下GridView的测量源码部分:
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Sets up mListPadding
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//取出测量规则和期望的宽度和高度
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode == MeasureSpec.UNSPECIFIED) {
            if (mColumnWidth > 0) {
                widthSize = mColumnWidth + mListPadding.left + mListPadding.right;
            } else {
                widthSize = mListPadding.left + mListPadding.right;
            }
            widthSize += getVerticalScrollbarWidth();
        }
//计算出出子view的宽度
        int childWidth = widthSize - mListPadding.left - mListPadding.right;
        boolean didNotInitiallyFit = determineColumns(childWidth);
//开始高度计算
        int childHeight = 0;
        int childState = 0;
//mAdapter中一共多少个子view
        mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
        final int count = mItemCount;
        if (count > 0) {
            final View child = obtainView(0, mIsScrap);

            AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
            if (p == null) {
                p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
                child.setLayoutParams(p);
            }
            p.viewType = mAdapter.getItemViewType(0);
            p.isEnabled = mAdapter.isEnabled(0);
            p.forceAdd = true;
//获取子view的测量规则进行测量
            int childHeightSpec = getChildMeasureSpec(
                    MeasureSpec.makeSafeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
                            MeasureSpec.UNSPECIFIED), 0, p.height);
            int childWidthSpec = getChildMeasureSpec(
                    MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width);
            child.measure(childWidthSpec, childHeightSpec);

            childHeight = child.getMeasuredHeight();
            childState = combineMeasuredStates(childState, child.getMeasuredState());

            if (mRecycler.shouldRecycleViewType(p.viewType)) {
                mRecycler.addScrapView(child, -1);
            }
        }
//进行高度的测量
        if (heightMode == MeasureSpec.UNSPECIFIED) {
            heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                    getVerticalFadingEdgeLength() * 2;
        }
//此处是关键,这里是我们使用的最大值模式,就是把所有的view都显示出来。也是我们去干预测量来实现自己想要的显示地方。
        if (heightMode == MeasureSpec.AT_MOST) {
            int ourSize =  mListPadding.top + mListPadding.bottom;

            final int numColumns = mNumColumns;
            for (int i = 0; i < count; i += numColumns) {
                ourSize += childHeight;
                if (i + numColumns < count) {
                    ourSize += mVerticalSpacing;
                }
                //我们传入的值很大,所以不会走到这里面。
                if (ourSize >= heightSize) {
                    ourSize = heightSize;
                    break;
                }
            }
            //最后的高度在这里生成。
            heightSize = ourSize;
        }

        if (widthMode == MeasureSpec.AT_MOST && mRequestedNumColumns != AUTO_FIT) {
            int ourSize = (mRequestedNumColumns*mColumnWidth)
                    + ((mRequestedNumColumns-1)*mHorizontalSpacing)
                    + mListPadding.left + mListPadding.right;
            if (ourSize > widthSize || didNotInitiallyFit) {
                widthSize |= MEASURED_STATE_TOO_SMALL;
            }
        }

        setMeasuredDimension(widthSize, heightSize);
        mWidthMeasureSpec = widthMeasureSpec;
    }

三种测量模式(网上摘取):

1、EXACTLY

精确值模式,当我们的控件的layout_width属性和layout_height属性指定为具体数值时,如android:layout_width="100dp",或者为match_parent时系统使用的是EXACTLY模式。

2、AT_MOST

最大值模式,当控件layout_width属性和layout_height属性为wrap_content时,控件大小随控件子控件或内容变化,此时控件的尺寸只要不超过父控件允许的最大尺寸即可。

3、UNSPECIFIED

不指定大小测量模式。
下面接着说具体的方法:

以上就是通过改变测量方式来完成全部展示GridView的item的方法。下面给出具体代码。

public class MyGridView extends GridView {
    public MyGridView(android.content.Context context,
                      android.util.AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 设置不滚动
     */
    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //第一个参数传入一个较大的值就可以了
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);

    }

}

结束语

就这么多了��。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值