欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Scroller弹性滑动

程序员文章站 2022-06-08 23:13:43
...
Android系统的滑动效果往往比较生硬,用户体验和粘着性方面如果要提高的话,弹性滑动是比较好的利器。实现

弹性滑动可以有好几种方式,其中利用Scroller实现弹性滑动是一种不错的方式。
首先,从源码层面看一下为什么Scroller为什么能实现弹性滑动。

   /**
     * 滚动到目标位置
     *
     * @param destX
     * @param destY
     */
    protected void smoothScrollTo(int destX, int destY) {
        int scrollX = mScroller.getFinalX()
        int scrollY = mScroller.getFinalY()
        int dx = destX - scrollX;
        int dy = destY - scrollY
        mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
        invalidate();
    }

    @Override
    public void computeScroll() {
        //判断mScroller滚动是否完成
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            //必须调用该方法,否则不一定能看到滚动效果
            postInvalidate();
        }
        super.computeScroll();
    }
smoothScrollTo(int destX, int destY)方法的两个参数分别是View子空间(内容)滑动的目标横坐标和纵坐标。

mScroller.getFinalX()表示的是运动前的横坐标,所以int dx = destX - mScroller.getFinalX()表示的就是运动的相对
距离。对于纵纵坐标也是如此。
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy),这个方法的参数有必要说明一下。第
一、二个参数分别表示的是View内容的原始坐标,第三、四个参数表示的是运动的相对距离(单位是像素),最后
一个参数表示的运动的时间长度。
invalidate()非常重要,只有调用这个方法才能后面的computeScroll()方法的调用,否则的话不一定会刷新界面,用户就会
看不到滚动效果。
再看computeScroll()方法,正是因为这个方法View才实现了弹性滑动。其中,mScroller.computeScrollOffset()返回的是一个
bool量,代表是否完成滑动,如果完成了滑动则返回true,否则返回false。
当View重绘后会在draw方法中调用computerScroll,而computerScroll又会去向Scroller获取当前的scrollX和scrollY;然后通过
scrollTo方法实现滑动;接着又调用postInvalidate方法来进行第二次重绘,这次重绘的过程和第一次重绘一样,还是会导致computerScroll
方法被调用;然后继续向scroller获取当前的scrollerX和scrollerY,并通过scrollTo方法滑动到新的位置,如是反复,直到整个滑动过程结束。
下面附上一段项目代码,该代码利用了Scroller实现了弹性滑动的LinearLayout布局视图。希望给大家带来帮助。

public class ElasticLinearLayout extends LinearLayout {

    private static final String TAG = ElasticLinearLayout.class.getSimpleName();
    private Scroller mScroller;
    private GestureDetector mGestureDetector;

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

    public ElasticLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setClickable(true);
        setLongClickable(true);
        mScroller = new Scroller(context);
        mGestureDetector = new GestureDetector(context, new GestureDetector.OnGestureListener(){
            @Override
        public boolean onDown(MotionEvent e) {
            // TODO Auto-generated method stub
            return true;
        }

        @Override
        public void onShowPress(MotionEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            // TODO Auto-generated method stub
            return false;
        }

        /**
         *
         * @param e1  第1个ACTION_DOWN MotionEvent
         * @param e2  最后一个ACTION_MOVE MotionEvent
         * @param distanceX  距离上次产生onScroll事件后,X抽移动的距离
         * @param distanceY  距离上次产生onScroll事件后,Y抽移动的距离
         * @return
         */
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            int disX = (int) ((distanceX - 0.5) / 2);
            int disY = (int) ((distanceY - 0.5) / 2);
            smoothScrollBy(disX, disY);
            return false;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            // TODO Auto-generated method stub
            return false;
        }
        });
    }

    /**
     * 滚动到目标位置
     *
     * @param destX
     * @param destY
     */
    protected void smoothScrollTo(int destX, int destY) {
        int dx = destX - mScroller.getFinalX();
        int dy = destY - mScroller.getFinalY();
        smoothScrollBy(dx, dy);
    }

    /**
     * 设置滚动的相对偏移
     *
     * @param dx
     * @param dy
     */
    protected void smoothScrollBy(int dx, int dy) {
        mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
        invalidate();
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                smoothScrollTo(0, 0);
                break;
            default:
                return mGestureDetector.onTouchEvent(event);
        }
        return super.onTouchEvent(event);
    }

}