关于Integer.MAX_VALUE>>2的骗局

前言:相信大家都解决过,ScrollView嵌套ListView只显示一行的问题,其解决方案大概是这样

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, 
        MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

MeasureSpec.makeMeasureSpec方法的作用是重新设置size跟mode,获取自己生成的heightMeasureSpec,在设置给super,达到修改高度的目的

1:那么为什么要设置 MeasureSpec.AT_MOST

查看ScrollView的源码:

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

super点进去:

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
               ......
            }
        }
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

查看ScrollView的measureChildWithMargins:

    @Override
    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int usedTotal = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin +
                heightUsed;
        final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
                Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
                MeasureSpec.UNSPECIFIED);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

可以看到ScrollView给ListView设置的高度mode为:MeasureSpec.UNSPECIFIED

查看ListView源码:


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

        if (heightMode == MeasureSpec.UNSPECIFIED) {
            heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                    getVerticalFadingEdgeLength() * 2;
        }

        if (heightMode == MeasureSpec.AT_MOST) {
            // TODO: after first layout we should maybe start at the first visible position, not 0
            heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
        }

        setMeasuredDimension(widthSize, heightSize);

        mWidthMeasureSpec = widthMeasureSpec;
    }

可知:

当heightMode == MeasureSpec.UNSPECIFIED的时候,ListView的高度仅为一个item的高度

当MeasureSpec.AT_MOST的时候,才会继续测量ListView的item的高度

所以我们手动给MeasureSpec.AT_MOST模式。

2:那么另一个问题是size为什么是Integer.MAX_VALUE >> 2呢?

大家可以去看所有的博客,都写的是Integer.MAX_VALUE >> 2

那么Integer.MAX_VALUE >> 2到底是有什么特殊的含义呢?用别的行不行

Integer.MAX_VALUE >> 2到底有没有道理,我认为这么写完全没有道理

为什么呢?

当MeasureSpec.AT_MOST的时候会执行measureHeightOfChildren,我们点进去

 final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,
            int maxHeight, int disallowPartialChildPosition) {
            ......
        for (i = startPosition; i <= endPosition; ++i) {
            ......
            returnedHeight += child.getMeasuredHeight();
            if (returnedHeight >= maxHeight) {
                // We went over, figure out which height to return.  If returnedHeight > maxHeight,
                // then the i'th position did not fit completely.
                return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
                            && (i > disallowPartialChildPosition) // We've past the min pos
                            && (prevHeightWithoutPartialChild > 0) // We have a prev height
                            && (returnedHeight != maxHeight) // i'th child did not fit completely
                        ? prevHeightWithoutPartialChild
                        : maxHeight;
            }
        return returnedHeight;
    }

这段代码告诉我们,我们设置的heightSize,在这里也就是maxHeight起到一个最大高度的作用,一旦child累计的高度超过了maxHeight了,ListView的高度就不会再继续改变了,带来的问题就是列表显示不全!

为了解决这个问题我们想到了给他设置一个比较大的数:Integer.MAX_VALUE 

Integer.MAX_VALUE 是int里正整数最大的了,好吧那我们试试:

发现竟然报错了,我们看看报的什么错:

哦,原来这里有限制,最大不能超过1073741823,那么1073741823这个数字又是什么呢?原来这个数字刚好等于Integer.MAX_VALUE右移1位

改成Integer.MAX_VALUE》1试一下,果然可以!!!

那位说了,你说的不对

人家MeasureSpec的用法是一个int值代表两个含义,你看

 public static final int AT_MOST = 2 << MODE_SHIFT;//MODE_SHIFT=30

2左移30位用二进制表示应该是:10000000000000000000000000000000

而前两位的10用以表达mode

后30位则是给size准备的,所以Integer.MAX_VALUE>>2的目的是给左边的10让出位置

但是

我想说的是,别忘了就算是Integer.MAX_VALUE也占不满32位,第一位是用来表示正数的0

    /**
     * A constant holding the maximum value an {@code int} can
     * have, 2^31-1.
     */
    @Native public static final int   MAX_VALUE = 0x7fffffff;

2^31-1换算成二进制为:

01111111111111111111111111111111

所以说第一位我已经让出,我再>>1,再让出一位完全够用,为什么我要>>2让出2位呢?

当然了Integer.MAX_VALUE>>2也不会有什么问题,毕竟就算Integer.MAX_VALUE>>2也足够大,足够我们使用了

但是这样不免让我们产生误解:Integer.MAX_VALUE>>2为最优写法

3:因此我说Integer.MAX_VALUE>>2没有道理

最优写法,应该是Integer.MAX_VALUE>>1,因为这样写会时刻提醒我们,这样写的原因所在!好了这就是本篇的全部内容,欢迎批评指正!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值