有趣的ViewPager

1、缘来

最近看到一个比较有意思的功能,看着像ViewPager的翻页,又像HorizontalScrollview的滑动。如下图所示:
这里写图片描述
联想到项目中刚做的一个类似的水平滑动,可以借鉴下,如果每次滑动都可以对齐图片边界,用户体验会不会更好点呢?

2、实现

毫无疑问,使用HorizontalScrollview肯定可以实现这样的功能,但是想一下应该需要处理一系列的触摸事件,判断滑动距离,自己去做图片边界对齐等等,想想就头大。上述功能看着像ViewPager的翻页,但是之前使用ViewPager基本都是一个item占满整个一屏,那么可不可以设置ViewPager一屏显示多个item呢?
很容易我们就找到了PagerAdapter的getPageWidth方法。首先,看看源码介绍。

/**
 * Returns the proportional width of a given page as a percentage of the
 * ViewPager's measured width from (0.f-1.f]
 *
 * @param position The position of the page requested
 * @return Proportional width for the given page position
 */
public float getPageWidth(int position) {
    return 1.f;
}

如上注释所示,getPageWidth方法,可以指定page item宽度相对于ViewPager整个宽度的占比,默认为1.0。这也就解释了为什么我们通常使用ViewPager时,每一页都占满整个屏幕了。找到方法,下一步就可以去实现了。

private float pageWidth;
public void computePageWidth(int type) {
    int width = screenWidth - ViewUtils.dip2px(context, 30);
    int computeWidth;
    if (type == 1) {
        computeWidth = (screenWidth - ViewUtils.dip2px(context, 40)) / 2;
    } else {
        computeWidth = (screenWidth - ViewUtils.dip2px(context, 50)) / 3;
    }
    pageWidth = computeWidth * 1.0f / width;
}

@Override
public float getPageWidth(int position) {
    return pageWidth;
}

computePageWidth是根据需求计算pageWidth。这里为啥会这么复杂,需要手动去计算每个item宽度占比呢?这是因为测试时ViewPager使用了setPageMargin方法,该方法可以很方便的指定page item之间的间距。而PagerAdapter的getPageWidth的占比计算并不包括pageMargin部分,因此就会出现问题。比如,假设ViewPager宽度为1000,setPageMargin设置为20,PagerAdapter的pageWidth设置为0.5f,那么每个item的最终宽度为500,而不是期望的(1000-20)/ 2 = 490。
查看ViewPager源码,看看到底是不是没有考虑到pageMargin。在ViewPager的onMeasure中,会分别测量每个item的宽度。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
            getDefaultSize(0, heightMeasureSpec));

    final int measuredWidth = getMeasuredWidth();
    final int maxGutterSize = measuredWidth / 10;
    mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);

    // Children are just made to fill our space.
    int childWidthSize = measuredWidth - getPaddingLeft() - getPaddingRight();
    int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();

    ......  

    // Page views next.
    size = getChildCount();
    for (int i = 0; i < size; ++i) {
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child
                    + ": " + mChildWidthMeasureSpec);

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if (lp == null || !lp.isDecor) {
                final int widthSpec = MeasureSpec.makeMeasureSpec(
                        (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY);
                child.measure(widthSpec, mChildHeightMeasureSpec);
            }
        }
    }
}

上述代码中的childWidthSize是ViewPager除去左右padding的内容区域宽度,也就是child的最大宽度,最终child宽度是childWidthSize * lp.widthFactor。这个widthFactor是ViewPager.LayoutParams中定义的宽度因子。在ViewPager的populate方法中,有这样一段代码对每个item的widthFactor进行了赋值操作。

// Check width measurement of current pages and drawing sort order.
// Update LayoutParams as needed.
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
    final View child = getChildAt(i);
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    lp.childIndex = i;
    if (!lp.isDecor && lp.widthFactor == 0.f) {
        // 0 means requery the adapter for this, it doesn't have a valid width.
        final ItemInfo ii = infoForChild(child);
        if (ii != null) {
            lp.widthFactor = ii.widthFactor;
            lp.position = ii.position;
        }
    }
}

可见,lp.widthFactor与ii.widthFactor直接相关。ItemInfo顾名思义就是每个item的一些信息,那么它又是什么时候赋值的呢?继续找,找到了ItemInfo初始化时的代码。

ItemInfo addNewItem(int position, int index) {
    ItemInfo ii = new ItemInfo();
    ii.position = position;
    ii.object = mAdapter.instantiateItem(this, position);
    ii.widthFactor = mAdapter.getPageWidth(position);
    if (index < 0 || index >= mItems.size()) {
        mItems.add(ii);
    } else {
        mItems.add(index, ii);
    }
    return ii;
}

很明显了,ItemInfo的widthFactor就等于PagerAdapter的pageWidth!果然,ViewPager没有考虑pageMargin的感受,看来这点设计上还是不够完美。因此,在设置了ViewPager的pageMargin情况下,需要我们手动去计算每个item的宽度占比。如果不想这么计算,可以将item之间的间距直接写在item布局中即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值