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

来源:互联网 发布:whatsapp mac 编辑:程序博客网 时间:2024/06/12 20:02

前话

今天公司项目遇到了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

阅读全文
0 0
原创粉丝点击