真-ViewPager与ScrollView间的ViewPager消失及滑动冲突关系

前话

今天公司项目遇到了ViewPager+ScrollView的布局方式,水平滑动+垂直滑动,可想而知可能会产生冲突。作为老司机,自信满满的准备好了解决冲突的方案,开撸。
写好运行一波后发现,尼玛啊,ViewPager不见了,说实话以前还真没有写过这种嵌套。没办法,试试将ViewPager固定一下高度呢,看看它存不存在。还真在,这下能看见了。可是不科学啊,我怎么知道页面数据有多少呢,固定值肯定行不通啊。
很自然的,打开浏览器—>谷歌一波。
搜索ViewPager+ScrollView嵌套ViewPager消失的网页很多,大多都一句话:设置ScrollView android:fillViewport=”true”!没想到这么简单,试试看!
果然,可行!网上说,用了这个之后ViewPager会无法滑动,我试了一下没出现啊,结果垂直方向滑不动了,妈的不按套路出牌。心想滑动冲突还不是smallCase。
于是按套路去解决了。至于套路是啥:无非分为外部解决和内部解决。具体方案本文不介绍了,出于:《安卓开发艺术探索》。网上也一大堆,可自行搜索。

ScrollView

经过一番折腾后发现并无卵用,他俩的事件分发完全没错,这就很尴尬了。那不是滑动的问题,会是啥呢?只能是控件本身高度就不够咯,嗯,测量出了问题。

翻翻源码吧,先看ScrollView

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

        if (!mFillViewport) {
            return;
        }

        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode == MeasureSpec.UNSPECIFIED) {
            return;
        }

        if (getChildCount() > 0) {
            final View child = getChildAt(0);
            final int widthPadding;
            final int heightPadding;
            final int targetSdkVersion = getContext()
            .getApplicationInfo().targetSdkVersion;
            final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if (targetSdkVersion >= VERSION_CODES.M) {
                widthPadding = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin;
                heightPadding = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin;
            } else {
                widthPadding = mPaddingLeft + mPaddingRight;
                heightPadding = mPaddingTop + mPaddingBottom;
            }

            final int desiredHeight = getMeasuredHeight() - heightPadding;
            if (child.getMeasuredHeight() < desiredHeight) {
                final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, widthPadding, lp.width);
                final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(desiredHeight, MeasureSpec.EXACTLY);
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

关键是:

 if (!mFillViewport) {
            return;
        }

这个mFillViewport看名字就很熟悉,就是刚刚我网上的方案咯,XML里面设置为True了。这里能看出来如果是false,直接返回了,相当于啥也没写,走父类的正常测量方式了。
为true的话,往下走,由于ScrollView只有一个子View,所以测量起来也很简单了。上面的代码用文字解释为:
首先获取儿子大小,如果儿子比自己小,把儿子撑到和自己一样大。如果儿子更大,就任由发展,如下:
这里写图片描述

难怪我最开始固定设置很大的时候能显示。那也就是说,滑不动是因为撑到一样大而已,并不能显示更多。换句话来说,不是“滑不动”,而是“没有了”。

ViewPager

ViewPager为什么高度不够呢,明明我在里面cang了很多东西啊,好吧,继续看ViewPager源码。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // For simple implementation, our internal size is always 0.
        // We depend on the container to specify the layout size of
        // our view.  We can't really know what it is since we will be
        // adding and removing different arbitrary views and do not
        // want the layout to change as this happens.
setMeasuredDimension(getDefaultSize(0,widthMeasureSpec),
        getDefaultSize(0, heightMeasureSpec));
        //省略儿子的测量代码
        }

卧槽,看到第一句我就惊呆了,直接按最初大小了,根本没有管Pager里面的控件,不管塞多少大小都是最初的(固定值:父布局分配的)。
好吧,终于找到原因了,我们重写onMeasure帮它测一下儿子的高度,再决定自己的高度。

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

        View maxHeightView = getChildAt(0);

        int size = getChildCount();
        //一个一个测量子View
        for (int i = 0; i < size; i++) {
            View child = getChildAt(i);
            if (child != null && child.getVisibility() != GONE) {
                child.measure(widthMeasureSpec, heightMeasureSpec);
                if (child.getHeight() > maxHeightView.getHeight()) {
                    maxHeightView = child;
                }
            }
        }
        //按viewpager里最大的那个高度算。
        if (maxHeightView != null) {
            setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, maxHeightView));
        }
    }

 private int measureHeight(int measureSpec, View view) {
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        //如果是固定值或match_parent
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = view.getMeasuredHeight();
            //wrap_content
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

运行,成功!果然百度谷歌不是万能的,有时候学会看源码也是很重要的,熟悉理解View绘制原理的话,解决起来一定不会很耗时。
至于滑动冲突,TMD我压根没遇到,应该是viewpager内部已经做好了处理吧,害我一开始就走错了方向。
参考文献:
http://smilehacker.com/zai-scrollviewzhong-shi-yong-viewpageryi-ji-viewpagershi-yong-wrap_content.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值