ViewPager一屏显示多个页面

使用ViewPager一般我们只需要一屏只显示一个页面,例如引导页、轮播图等。但它如何能一屏显示多个页面呢?其实很简单就是在控件的父布局或根布局添加clipChildren=false属性即可。

android:clipChildren属性:是否限制子控件在其范围内显示,也就是说当子控件超出父控件规定大小时,超出部分是否显示,clipChildren默认值为true。 也就是不允许超出绘制

效果图

这里写图片描述

示例代码

1:布局文件

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    android:gravity="center">

    <RelativeLayout
        android:id="@+id/root"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:clipChildren="false"
        android:gravity="center">

        <com.cn.lyz.viewpagegallery.viewpage_all_response.ViewPagerAllResponse
            android:id="@+id/vp"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_centerHorizontal="true"
            android:clipChildren="false"/>

    </RelativeLayout>

</RelativeLayout>

2:设置切换动画

public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
    public static final float MIN_SCALE = 0.8f;//原图片缩小0.8倍
    private static final float MIN_ALPHA = 0.6f;//透明度

    public void transformPage(View page, float position) {
        if (position < -1) {//[-Infinity,-1)左边显示出半个的page
            page.setAlpha(MIN_ALPHA);//设置page的透明度
            page.setScaleX(MIN_SCALE);
            page.setScaleY(MIN_SCALE);
        } else if (position <= 1) {
            if (position < 0) {//(0,-1] 第一页向左移动
                if (position < -0.2f)//最大缩小到0.8倍
                    position = -0.2f;
                page.setAlpha(1f + position*2);
                page.setScaleY(1f + position);
                page.setScaleX(1f + position);
            } else {//[1,0] 第二页向左移动 成currentItem
                if (position > 0.2)
                    position = 0.2f;
                page.setAlpha(1f -position*2);
                page.setScaleY(1f - position);
                page.setScaleX(1f - position);
            }
        } else {//(1,+Infinity]右边显示出半个的page
            page.setAlpha(MIN_ALPHA);
            page.setScaleX(MIN_SCALE);
            page.setScaleY(MIN_SCALE);
        }
    }
}

postion主要分为

  1. [-Infinity,-1)
  2. [-1,1]
  3. (1,+Infinity]

第一个对应左边显示一点的page,第二个对应中间显示的page,第三个对应右边显示一点的page。
第一个和第三个只需要设置最小值就可以。
第二个还需要细分为(0,-1]和[1,0]

由第一页滑动到第二页来说:
页1的position变化为:从0到-1
页2的position变化为:从1到0

由于Scale缩放倍数是0.8,所以在页面滑动时对position做了一些限制,使其不超过0.8
由于ALPHA透明倍数是0.6,所以在[-1,1]之间需要乘以2,否则透明度过度会很突兀

3:自定义Viewpager

捕获viewpager的父控件的触摸事件,并传递给viewpager,用于全屏滑动

mRoot.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return mVp.dispatchTouchEvent(event);
            }
        });
public class ViewPagerAllResponse extends ViewPager {
    private final static float DISTANCE = 10;//默认距离
    private float downX;
    private float downY;
    private float upX;
    private float upY;

    public ViewPagerAllResponse(Context context) {
        this(context, null);
    }

    public ViewPagerAllResponse(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            downX = ev.getX();
            downY = ev.getY();
        } else if (ev.getAction() == MotionEvent.ACTION_UP) {
            upX = ev.getX();
            upY = ev.getY();
            //滑动时进入
            if (Math.abs(upX - downX) > DISTANCE || Math.abs(upY - downY) > DISTANCE) {
                return super.dispatchTouchEvent(ev);
            }
            //点击时进入
            View view = clickPageOnScreen(ev);
            if (view != null) {
                int index = (Integer) view.getTag();
                if (getCurrentItem() != index) {
                    setCurrentItem(index);
                    return true;
                }
            } else {
                return true;
            }
        }
        return super.dispatchTouchEvent(ev);
    }

    /**
     * 是否点击到两边的View
     * 点到View则返回该View,否则返回null
     */
    private View clickPageOnScreen(MotionEvent ev) {
        int childCount = getChildCount();
        int currentIndex = getCurrentItem();
        int[] location = new int[2];
        float x = ev.getRawX();

        for (int i = 0; i < childCount; i++) {
            View v = getChildAt(i);
            int position = (Integer) v.getTag();
            v.getLocationOnScreen(location);
            int minX = location[0];
            int maxX = location[0] + v.getWidth();

            if (position < currentIndex) {
                //maxX-view宽度*View缩放了宽度的一半
                maxX -= v.getWidth() * (1 - ZoomOutPageTransformer.MIN_SCALE) * 0.5;
                minX -= v.getWidth() * (1 - ZoomOutPageTransformer.MIN_SCALE) * 0.5;
            }

            if ((x > minX && x < maxX)) {
                return v;
            }
        }
        return null;
    }

    /**
     * 解决当手指在第1页左边空白区域上下滑动时,不让page的点击事件触发
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = ev.getY();
                break;
            case MotionEvent.ACTION_UP:
                upY = ev.getY();
                if (Math.abs(upY - downY) > DISTANCE) {
                    return true;//拦截事件 传递给自己的onTouchEvent处理
                }
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = ev.getY();
                break;
            case MotionEvent.ACTION_UP:
                upY = ev.getY();
                if (Math.abs(upY - downY) > DISTANCE) {
                    return super.onTouchEvent(ev);//不消费事件,事件继续往上传递
                }
                break;
            default:
                break;
        }
        return super.onTouchEvent(ev);
    }
}
  1. 复写dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent方法,解决在两侧page页上下滑动时,响应点击事件,即阻止了page的点击事件触发
  2. 复写dispatchTouchEvent方法,在其clickPageOnScreen方法内判断是否为中间page的点击事件,如果是则响应点击事件,否则则只设置setCurrentItem(index)使其跳到中间

4:Activity中设置

ViewpagerAdapter adapter = new ViewpagerAdapter(this, mVp, list);
mVp.setAdapter(adapter);
//设置缓存数为展示的数目
mVp.setOffscreenPageLimit(list.size());
//设置切换动画
mVp.setPageTransformer(true, new ZoomOutPageTransformer());
//设置Page间间距
mVp.setPageMargin(0);

注意
不设置setPageMargin大小两个page页也是有间距的
间距大小=page原大小*(1-缩放倍数)/2
如图
这里写图片描述

总结

其实效果实现起来并不难,只要理解了clipChildren属性的作用,再自定义一个PageTransformer过渡动画,ViewPager的各种效果也就不难实现了。

当然了,如果你不想用clipChildren属性,或者说使用继承PagerAdapter的方式定义布局不方便,则可以使用Viewpager添加Fragment方式来实现。效果图和上面一样就不再放了,所有实现方式再源码中都有,感兴趣可以戳源码下载。

源码下载
Demo下载

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值