Scroller的fling方法详解

Scroller概述

  • scroller的作用:
    作为一个developer,要有看官方文档的习惯,文档链接。
    关于scroller的作用我就不叙述了,google一下有太多的介绍

案例分析

自定义一个viewgroup,在viewgroup里面有一个能滑动的view,且手指必须按在view上才能生效,松手后能缓慢的停下来;

  • 分析:滑动view首先想到的是使用Scrooler(ViewDragHelper及属性动画咱先不考虑)。滑动,首先想到的是startScroll方法。
    咱们来看看这个方法
    startScroll(int startX, int startY, int dx, int dy)
    startScroll(int startX, int startY, int dx, int dy, int duration)
    这两个方法的区别就是多了个duration,即需要滑动到目的位置需要的时间,类似animation里面的duration
    看源码:
public void startScroll(int startX, int startY, int dx, int dy) {
        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
    }

其实最后调用的还是第二个方法,默认的滑动时间为250ms;
调用如下方法:

view.startScroll(getScroolX(), getScrollY(), -50, -50, )

代码意思为:从当前的scroolX scroolY,x轴移动50个像素,y轴移动50个像素。

  • 但是我们的需求是需要松开手指后能继续滑动
    所以,这里就得使用fling方法了,我们来看看fling方法的具体内容:
fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY)
Start scrolling based on a fling gesture.

由官网的解释来看,就知道了这是基于一个滑动手势的滑动
OK,这里我们需要用到另外一个计算速度的类VelocityTracker,关于使用方法请自行google

  • 定位view的具体位置:
    如果我们只是如下操作:
    按下—>滑动—->抬起手指 —–>再按下—->滑动……
    这类操作的话,那么我们很容易定位到view的具体位置,但是,我们需要的是在手指抬起后还能滑动,那么,此时我们怎么定位view的位置呢;

  • 这里我们就需要了解下Scroller的坐标系问题了
    一般来说,对于viewgroup,我们我获取里面的子view的坐标只需要getLeft和getTop来就能知道当前view的x,y坐标。
    那么Scroller的getScrollX()getScrollY()getFinalX(),getFinalY(),这里的获取的X和Y到底是什么呢,通过初次打印log信息,发现得到的x,y值有点莫名其妙,跟view的坐标系好像没什么关系,但是通过滑动我们来看获取的scrolllX坐标的规律,不难明白,这里获取的scrolllX和scrollY是基于你需要滑动的那个view的位置的。

    光说可能不好明白,有图为证:
    Scroller的fling方法详解_第1张图片

  • 到了这里,咱们就能很容易的搞定fling的状态下view的定位问题

贴上全部代码:

public class HoodleGroupView extends ViewGroup {

    private PointF mDownPoint;

    private RectF mChildeRectF;
    private VelocityTracker velocityTracker;
    private Scroller mScroller;
    private int mMaxFlintVelocity, mMinFlintVelocity;
    private int mChildMeasuredWidth,mChildMeasuredHeight;
    private View chileView;
    private boolean isFirPoint = true;
    private float lastX, lastY;

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

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

    public HoodleGroupView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initData(context);
    }

    private void initData(Context context) {
        mDownPoint = new PointF();
        mChildeRectF = new RectF();
        mScroller = new Scroller(context, null, true);
        ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
        mMaxFlintVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
        mMinFlintVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
        mMinFlintVelocity = 600;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        LogUtil.e("onLayout");
        chileView = getChildAt(0);
        int measuredWidth = getMeasuredWidth();
        int measuredHeight = getMeasuredHeight();
        mChildMeasuredWidth = chileView.getMeasuredWidth();
        mChildMeasuredHeight = chileView.getMeasuredHeight();
        mChildeRectF.set(measuredWidth / 2 - mChildMeasuredWidth / 2, measuredHeight / 2 - mChildMeasuredHeight / 2, measuredWidth / 2 + mChildMeasuredWidth / 2, measuredHeight / 2 + mChildMeasuredHeight / 2);
        chileView.layout(measuredWidth / 2 - mChildMeasuredWidth / 2, measuredHeight / 2 - mChildMeasuredHeight / 2, measuredWidth / 2 + mChildMeasuredWidth / 2, measuredHeight / 2 + mChildMeasuredHeight / 2);
    }

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

    }

    @Override
    public void onHoverChanged(boolean hovered) {
        LogUtil.e("onHoverChanged");
        super.onHoverChanged(hovered);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        LogUtil.e("onScrollChanged,l=" + l + ",t=" + t + ",oldl=" + oldl + ",oldt=" + oldt);
//        mChildeRectF.set(mChildeRectF.left - l, mChildeRectF.top - t, mChildeRectF.right - l, mChildeRectF.bottom - t);
        super.onScrollChanged(l, t, oldl, oldt);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
//        return super.onTouchEvent(event);
        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain();
        }
        velocityTracker.addMovement(event);
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mDownPoint.x = event.getX();
                mDownPoint.y = event.getY();
                lastX = event.getX();
                lastY = event.getY();
                LogUtil.e("down_x=" + lastX + ",down_y=" + lastY);
                //判断按下的手指是否在图片上面
                if(!isViewUnderPoint(mDownPoint)){
                    return false;
                }
                if(!mScroller.isFinished()){
                    mScroller.abortAnimation();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                float ev_x = event.getX();
                float ev_y = event.getY();
                scrollBy((int) (-ev_x + lastX), (int) (-ev_y + lastY));
//                mChildeRectF.set(mChildeRectF.left + ev_x - lastX, mChildeRectF.top + ev_y - lastY, mChildeRectF.right + ev_x - lastX, mChildeRectF.bottom + ev_y - lastY);
                lastX = ev_x;
                lastY = ev_y;
                break;
            case MotionEvent.ACTION_UP:
                //手指抬起,计算当前速率
                float up_x = event.getX();
                float up_y = event.getY();
                velocityTracker.computeCurrentVelocity(1000, mMaxFlintVelocity);
                int xVelocity = (int) velocityTracker.getXVelocity();
                int yVelocity = (int) velocityTracker.getYVelocity();
                int scrollX = getScrollX();
                int scrollY = getScrollY();
                LogUtil.e("mMinFlintVelocity=" + mMinFlintVelocity + ",xVelocity=" + xVelocity + ",yVelocity=" + yVelocity);
                if (Math.abs(xVelocity) > mMinFlintVelocity && Math.abs(yVelocity) > mMinFlintVelocity) {
                    mScroller.fling(scrollX, scrollY, -xVelocity, -yVelocity, 0, getWidth() - chileView.getWidth(), 0, getHeight() - chileView.getHeight());
                    LogUtil.e("up_x=" + up_x + ",up_y=" + up_y + "scrollX=" + scrollX + ",scrollY=" + scrollY + ", startX=" + mScroller.getStartX() + ", startY=" + mScroller.getStartY() + ", width=" + getWidth() + ",childWidth=" + chileView.getWidth() + ",height=" + getHeight() + ",childheight=" + chileView.getHeight());
                    int startX = mScroller.getStartX();
                    int startY = mScroller.getStartY();
                    int finalX = mScroller.getFinalX();
                    int finalY = mScroller.getFinalY();
                    mChildeRectF.set(chileView.getLeft() - finalX, chileView.getTop() -finalY, chileView.getRight() - finalX, chileView.getBottom() -finalY);
                    int dex_x, dex_y;
                    LogUtil.e("upx=" + up_x + ",upy=" + up_y + ",pontX=" + mDownPoint.x + ",pontY=" + mDownPoint.y);
                    if(up_x > mDownPoint.x) {
                        //证明往右滑动
                        dex_x = finalX - startX;
                    }else {
                        dex_x = startX - finalX;
                    }
                    if (up_y > mDownPoint.y) {
                        dex_y = finalY - startY;
                    }else {
                        dex_y = startY - finalY;
                    }
                    LogUtil.e("dex="+dex_x+",dey="+dex_y+"left="+mChildeRectF.left+",top="+mChildeRectF.top+"finalX=" + mScroller.getFinalX() + ",finalY=" + mScroller.getFinalY());
//                    mChildeRectF.set(mChildeRectF.left + dex_x, mChildeRectF.top + dex_y, mChildeRectF.right + dex_x, mChildeRectF.bottom + dex_y);
                    LogUtil.e("left="+mChildeRectF.left+",top="+mChildeRectF.top);
                    awakenScrollBars(mScroller.getDuration());
                    invalidate();
                }else {
                    mChildeRectF.set(chileView.getLeft() - scrollX, chileView.getTop() -scrollY, chileView.getRight() - scrollX, chileView.getBottom() -scrollY);
                }

                if (velocityTracker != null) {
                    velocityTracker.clear();
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                LogUtil.e("-----ACTION_CANCEL");
                break;
        }
        return true;
    }

    private boolean isViewUnderPoint(PointF pointF) {
        return mChildeRectF.contains(pointF.x, pointF.y);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            LogUtil.e("computeScroll---"+mScroller.getCurrX()+","+mScroller.getCurrY());
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }
}

你可能感兴趣的:(Android)