android 群英传笔记----Android scroll

现在流行的app中都存在大量的滑动操作,但是一直没有去研究过,实在是不应该,正好这本书有讲解,就参考本书学习一下

  • 滑动产生的原因
  • 如何实现、处理滑动效果
1 . 滑动产生的原因

滑动一个View,本质上来说就是移动一个View。改变其当前所处位置,它的原理与动画效果非常相似,都是通过不断改变View的坐标来实现这一效果。所以,要实现View的滑动,就必须监听用户的触摸事件,从而实现View跟随用户触摸的滑动而滑动。

1.1 坐标系

如下图所示:
这里写图片描述

1.2 常见的获取坐标方法

这里写图片描述

2 .实现滑动的7中方法

要实现的效果:让自定义View随着手指在屏幕上的滑动而滑动

  • layout()方法
public class DragViewLayout extends View {

    private int lastX;
    private int lastY;

    public DragViewLayout(Context context) {
        super(context);
    }

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

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


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y-lastY;
                layout(getLeft()+offsetX,getTop()+offsetY,getRight()+offsetX,getBottom()+offsetY);
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;

        }
        return true;
    }
}
  • offsetLeftAndrRight()与offsetTopAndBottom()
public class DragViewOffset extends View {

    private int lastX;
    private int lastY;

    public DragViewOffset(Context context) {
        super(context);
    }

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

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


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y-lastY;
                offsetLeftAndRight(offsetX);
                offsetTopAndBottom(offsetY);
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return true;
    }
}
  • LayoutParams
public class DragViewMargin extends View {

    private int lastX;
    private int lastY;
    private ViewGroup.MarginLayoutParams params;

    public DragViewMargin(Context context) {
        super(context);
    }

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

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


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y-lastY;
                params = (ViewGroup.MarginLayoutParams) getLayoutParams();
                params.leftMargin = getLeft()+offsetX;
                params.topMargin = getTop()+offsetY;
                setLayoutParams(params);
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return true;
    }
}
  • scrollTo 与scrollby,注意参考系的不同
/**
* 如果是View使用Scrollby,移动的是view的内容,TextView,content是他的文本,ImageView,content是他的drawable
* 如果是ViewGroup,移动的是所有子View
*/
public class DragViewScrollBy extends View {

    private int lastX;
    private int lastY;
    private ViewGroup.MarginLayoutParams params;

    public DragViewScrollBy(Context context) {
        super(context);
    }

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

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


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                //此处,是用-offset
                ((ViewGroup) getParent()).scrollBy(-offsetX, -offsetY);
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return true;
    }
}
  • Scroller,实现平滑移动效果
/**
 * Created by acer on 2015/11/5.
 * 松开手指,回到原位置
 * 
 */
public class DragViewScroller extends View {

    private int lastX;
    private int lastY;
    private Scroller mscroller;

    public DragViewScroller(Context context) {
        super(context);
        init(context);
    }

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

    public DragViewScroller(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        mscroller = new Scroller(context);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                ((View) getParent()).scrollBy(-offsetX, -offsetY);
                break;
            case MotionEvent.ACTION_UP:
            //手指离开,开始滑动回原位置
                View viewGroup = (View) getParent();
                mscroller.startScroll(viewGroup.getScrollX(), viewGroup.getScrollY(), -viewGroup.getScrollX(), -viewGroup.getScrollY());
                invalidate();
                break;
            default:
                break;
        }
        return true;
    }
    /**
    *computeScroll()方法不会自动调用,
    *只能通过invalidate()---draw()--computeScroll()来间接调用
    *
    */
    @Override
    public void computeScroll() {
        if (mscroller.computeScrollOffset()) {
            ((View) getParent()).scrollTo(mscroller.getCurrX(), mscroller.getCurrY());
            //通过重绘,不断调用computeScrol
            invalidate();
        }
    }
}
  • ViewGragHelper 强大的滑动控制类,实现各种不同的滑动,拖放需求
public class LearnViewDragHelper extends FrameLayout {

    private ViewDragHelper mHelper;
    private View mMenuView;
    private View mMainView;
    private int mWidth;


    public LearnViewDragHelper(Context context) {
        super(context);
        init();
    }

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

    public LearnViewDragHelper(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public LearnViewDragHelper(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }


    /**
     * 加载布局文件完成后调用
     */
    @Override
    public void onFinishInflate(){
        super.onFinishInflate();
        mMenuView = getChildAt(0);
        mMainView = getChildAt(1);
    }

    @Override
    public void onSizeChanged(int w,int h,int oldw,int oldh){
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = mMenuView.getMeasuredWidth();
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //将触摸事件传递给ViewDragHelper,此操作必不可少
        mHelper.processTouchEvent(event);
        return true;
    }



    private void init() {
        mHelper = ViewDragHelper.create(this, callback);
    }


    /**
     * 核心回調
     **/
    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
        /**
         * 开始检测触摸事件
         * @param child
         * @param pointerId
         * @return
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            //如果当前触摸的child是mMainView时开始检测
            return mMainView==child;
        }

        /**
         * 用户触摸到View后回调
         * @param capturedChild
         * @param activePointerId
         */
        @Override
        public void onViewCaptured(View capturedChild, int activePointerId) {
            super.onViewCaptured(capturedChild, activePointerId);
        }

        /**
         * view拖拽状态改变时回调
         * @param state
         */
        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }

        /**
         * view position改变时回调,常用于更改scale进行缩放等效果
         * @param changedView
         * @param left
         * @param top
         * @param dx
         * @param dy
         */
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
        }

        /**
         *
         * 通常情况下,只需要返回top 和 left即可,但是当需要更加精确的计算padding等属性的时候,就需要对left进行一些处理,并返回合适大小的值
         * */


        /**
         * 水平方向滑动
         * @param child
         * @param left 在水平方向child移动的距离
         * @param dx 较前一次的增量
         * @return 默认返回0
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;
        }

        /**
         * 竖直方向滑动
         * @param child
         * @param top 在竖直方向child移动的距离
         * @param dy 较前一次移动的距离
         * @return 默认返回0
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return super.clampViewPositionVertical(child,top,dy);
        }

        /**
         * 拖动结束后调用
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            //手指抬起后,缓慢移动到指定位置
            if(mMainView.getLeft()<500){
                //关闭菜单,相当于Scroller.startScroll
                mHelper.smoothSlideViewTo(mMainView,0,0);
                ViewCompat.postInvalidateOnAnimation(LearnViewDragHelper.this);
            }else{
                //打开菜单
                mHelper.smoothSlideViewTo(mMainView,300,0);
/**
*Cause an invalidate to happen on the next animation time *step, typically the next display frame
*
*/                ViewCompat.postInvalidateOnAnimation(LearnViewDragHelper.this);
            }
        }


    };



    /**
     * Move the captured settling view by the appropriate amount for the current time.
     * If <code>continueSettling</code> returns true, the caller should call it again
     * on the next frame to continue.
     *
     * @param deferCallbacks true if state callbacks should be deferred via posted message.
     *                       Set this to true if you are calling this method from
     *                       {@link android.view.View#computeScroll()} or similar methods
     *                       invoked as part of layout or drawing.
     * @return true if settle is still in progress
     */
    @Override
    public void computeScroll() {
        if (mHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }


}

滑动效果图如下:

这里写图片描述

DragViewHelper效果图如下:

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值