Scroller的使用 仿ViewPager效果

Posted 怪兽N

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scroller的使用 仿ViewPager效果相关的知识,希望对你有一定的参考价值。

简介

首先要知道Scroller或者OverScroller是用来计算滚动用的, 再具体点说就是使用触摸事件生成滚动动画所需的数据,实现以动画方式显示滚动手势。通过Scroller不断更新滚动数据后,还是要调用View.scrollTo(x,y)或View.scrollBy(x,y)来控制View的内容滚动。 其次,Scroller或者OverScroller作用和使用方式基本是一样的,但是OverScroller包含了响应方法来向用户指示其在执行平移或滑动手势后已达到边缘,一般配合EdgeEffect类一起使用,显示滑动到边界的效果。Google官方建议使用OverScroller代替。

代码地址:https://gitee.com/guaishoun/scroller-pager-sample.git

使用Scroller的三个步骤

定义滚动,触发滚动,然后不断更新滚动。

1 定义滚动

public Scroller(Context context, Interpolator interpolator, boolean flywheel)

虽然还有其他两个构造函数,但是都是会调用这个构造函数的,插值器interpolator如果不定义则使用系统默认的粘性流动插值器ViscousFluidInterpolator。flywheel效果的意思是在原来的滚动状态下,叠加滚动效果,简单点理解就是玩转盘的时候,转盘还没停就用手去再拨动一下,然后就继续滚了。flywheel 默认在android3.0以后都是默认支持的了。

2 触发滚动

触发滚动的方式可以使用start或者fling的方式。

start方式

/**
 * startX 开始滚动的X
 * startY 开始滚动的Y
 * dx,dy 要滚动的距离
 * duration 滚动时间,DEFAULT_DURATION = 250ms
 */
public void startScroll(int startX, int startY, int dx, int dy, int duration)

//使用例,在onTouchEvent(MotionEvent event)中的MotionEvent.ACTION_UP触发
case MotionEvent.ACTION_UP:
    {
        mScroller.startScroll(startX,startY,dx,dy);
        invalidate();
    }

fling方式, 这种方式滚动距离由设置的初始值、初始速度、摩擦力共同决定

/**
 * startX 开始滚动的X
 * startY 开始滚动的Y
 * velocityX, velocityY初始速度
 * minX、maxX、minY、maxY,限制,到达则停止
 * 
 * 注意:
 * 摩擦力没有在这个触发方法,但是系统是用有默认值的
 * mFlingFriction = ViewConfiguration.getScrollFriction()
 * 也可以setFriction
 */
public void fling(int startX, int startY, int velocityX, int velocityY,int minX, int maxX, int minY, int maxY)
    
//使用例,在onTouchEvent(MotionEvent event)中的MotionEvent.ACTION_UP触发
case MotionEvent.ACTION_UP:
    {
        float velocityX = velocityTracker.getXVelocity(pointerId);
        float velocityY = velocityTracker.getYVelocity(pointerId);
        mScroller.fling(startX,startY,velocityX,velocityY,minX,maxX,minY,maxY)    				invalidate();
    }

3 更新滚动

要知道Scroller的每次更新要调用它的computeScrollOffset()。那么可以利用computeScroll函数会在View的draw中回调,更新滚动数据。下面那样

public class ScrollerPager extends ViewGroup {
    .....        
	@Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            int x = mScroller.getCurrX();
            scrollTo(x,0);
            invalidate();
        }
    }
    ....
}

对应示例代码

public class ScrollerPager extends ViewGroup {
    public static final String TAG = ScrollerPager.class.getSimpleName();
    private final Scroller mScroller;
    private final float mTouchSlop;
    private MotionEvent preMotion;
    private int leftBorder,rightBorder;
    private int index;

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

    public ScrollerPager(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ScrollerPager(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr,0);
    }

    public ScrollerPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mScroller = new Scroller(context);
        ViewConfiguration vc = ViewConfiguration.get(context);
        mTouchSlop = vc.getScaledTouchSlop()*0.8f;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            measureChild(child,widthMeasureSpec,heightMeasureSpec);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.layout(i*child.getMeasuredWidth(),0,(i+1)*child.getMeasuredWidth(),child.getMeasuredHeight());
        }
        //边界
        leftBorder = getChildAt(0).getLeft();
        rightBorder = getChildAt(getChildCount() - 1).getRight();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        final int action = event.getAction() & MotionEvent.ACTION_MASK;
        onTouchEvent(event);
        if(action == MotionEvent.ACTION_DOWN){
            preMotion = MotionEvent.obtain(event);
        }
        if(action == MotionEvent.ACTION_MOVE && mTouchSlop < calculateMoveDistance(event, preMotion)){
            return true;
        }
        return super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction()&MotionEvent.ACTION_MASK;
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                preMotion = MotionEvent.obtain(event);
                break;
            case MotionEvent.ACTION_MOVE:
                float deltaScroll = preMotion.getX()-event.getX();
                if (getScrollX() + deltaScroll < leftBorder) {
                    scrollTo(leftBorder, 0);
                } else if (getScrollX() + getWidth() + deltaScroll > rightBorder) {
                    scrollTo(rightBorder - getWidth(), 0);
                }else{
                    scrollBy((int)deltaScroll,0);
                }
                preMotion = MotionEvent.obtain(event);
                break;
            case MotionEvent.ACTION_UP:
                //start scroll and invalidate
                index = (getScrollX()*2/getChildAt(0).getWidth()+1)/2;
                mScroller.startScroll(getScrollX(),0,getChildAt(index).getLeft() - getScrollX(),0);
                invalidate();
                preMotion.recycle();
                break;
            default:
                break;
        }
        return true;
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            int x = mScroller.getCurrX();
            scrollTo(x,0);
            invalidate();
        }
    }

    /**
     * 计算两点
     */
    private float calculateMoveDistance(MotionEvent event1 , MotionEvent event2){
        if (event1==null || event2==null){
            return 0f;
        }
        float disX = Math.abs(event1.getRawX() - event2.getRawX());
        float disY = Math.abs(event1.getRawX() - event2.getRawX());
        return (float) Math.sqrt(disX * disX + disY * disY);
    }
}

以上是关于Scroller的使用 仿ViewPager效果的主要内容,如果未能解决你的问题,请参考以下文章

android ViewPager 进阶(仿画廊/图书翻页) 与 palette 使用 (含完整Demo)

ViewPager,ViewPager2 无限轮播功能。自定义 Indicator,支持一屏三页,支持仿魅族 banner 效果。极其简单的使用方式

ViewPager 仿 Gallery效果

ViewPager+GridView实现横向滑动 仿大众点评

android仿网易云音乐引导页仿书旗小说Flutter版ViewPager切换爆炸菜单风扇叶片效果等源码

Listview嵌套Viewpager实现仿淘宝搜狐广告主页,并实现listview的下拉刷新