Scroll滑动效果

《群英传》笔记:

概述:滑动的本质是改变View当前所处的位置,通过不断地改变坐标来实现这一效果。所以滑动方法就是监听用户的触摸事件,根据事件传入的目标,不断地改变View的坐标。

Android:坐标系:在Android中,坐标原点在屏幕的左上角(游戏是左下角),向左为X轴正方向,向下为Y轴正方向。系统提供getLocationOnScreen(intlocation[])这样的方法来获取坐标系中点的位置。在触控事件中使用getRawX()、getRawY()获得Android坐标系坐标 。

视图坐标系:描述子视图在父视图中位置关系。坐标原点是以父视图左上角为坐标原点,通过getX()、getY()获得视图坐标系中的坐标。

触控事件(MotionEvent):
通常情况下,通过onTouchEvent(MotionEvent event)方法中通过event.getAction()方法来获取触控事件的类型,使用switch-case方法进行筛选。

模板代码:

public boolean onTouchEvent(MotionEvent event) {
        // 获取当前输入点的X、Y视图坐标
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 处理输入的按下事件
                break;
            case MotionEvent.ACTION_MOVE:
                // 处理输入的移动事件
                break;
case MotionEvent.ACTION_UP:
                // 处理输入的离开事件
                break;
        }
        return true;
}

实现滑动的七种方法:

1、layout方法:

public class ScrollLayout1 extends View{
    private int lastX;
    private int lastY;

    public ScrollLayout1(Context context) {
        super(context);
        ininView();
    }

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

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

    private void ininView() {
        // 给View设置背景颜色,便于观察
        setBackgroundColor(Color.BLUE);
    }

    // 视图坐标方式
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 获取当前输入点的X、Y视图坐标
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录触摸点坐标
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算偏移量
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                // 在当前left、top、right、bottom的基础上加上偏移量
                layout(getLeft() + offsetX,
                        getTop() + offsetY,
                        getRight() + offsetX,
                        getBottom() + offsetY);
                break;
        }
        return true;
    }
}

2、offsetLeftAndRight()和offsetTopAndBottom():

// 同时对LeftRight进行偏移
offsetLeftAndRight(offsetX);
// 同时对TopBottom进行偏移
offsetTopAndBottom(offsetY);

3、LayoutParams:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录触摸点坐标
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算偏移量
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                // 使用ViewGroup.MarginLayoutParams不需要考虑父布局的类型。
                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
                layoutParams.leftMargin = getLeft() + offsetX;
                layoutParams.topMargin = getTop() + offsetY;
                setLayoutParams(layoutParams);
                break;
        }
        return true;
}

4、scrollTo和scrollBy:

scrollTo(x,y)表示移动到一个具体的坐标点(x,y),而scrollBy(dx,dy)表示移动的增量为dx、dy。

@Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            lastX = (int) event.getX();
            lastY = (int) event.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            int offsetX = x - lastX;
            int offsetY = y - lastY;
            // 偏移量必须为负值。若为正值,因为参考系选择的不同会产生不同的效果。
            ((View) getParent()).scrollBy(-offsetX, -offsetY);
            break;
        }
        return true;
    }

5、Scroller:
scrollTo和scrollBy在事件执行的时候会瞬间平移完成,而Scroller类可以实现平滑移动的效果。原理是利用了人眼的视觉暂留特性,就像翻小人书一样,很多个微小瞬间偏移量集合在一块就可以获得平滑移动的效果。

三步骤:

public class ScrollScroller extends View{
    private int lastX;
    private int lastY;
    private Scroller mScroller;

    public ScrollScroller(Context context) {
        super(context);
        ininView(context);
    }

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

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

    private void ininView(Context context) {
        setBackgroundColor(Color.BLUE);
        // 第一步:初始化Scroller
        mScroller = new Scroller(context);
    }

    // 第二步:重写computeScroll()方法,实现模拟滑动。核心内容
    @Override
    public void computeScroll() {
        super.computeScroll();
        // 判断Scroller是否执行完毕,computeScrollOffset判断是否完成了整个滑动
        if (mScroller.computeScrollOffset()) {
            ((View) getParent()).scrollTo(
                    // 获得当前的滑动坐标
                    mScroller.getCurrX(),
                    mScroller.getCurrY());
            // 通过重绘来不断调用computeScroll
            invalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                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;
        }
        return true;
    }
}

6、属性动画
这一块放在Android动画模块里。

7、ViewDragHelper:
基本可以实现各种不同的滑动、拖放需求,功能强大意味着复杂。

package com.android.scroll;

import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;

/**
 * 滑动侧边栏布局
 * 
 * @author Administrator
 *
 */
public class DragViewGroup extends FrameLayout {

    private ViewDragHelper mViewDragHelper;
    private View mMenuView, mMainView;
    private int mWidth;

    public DragViewGroup(Context context) {
        super(context);
        initVew();
    }

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

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

    // 第一步:初始化ViewDragHelper,定义在ViewGroup的内部,通过其静态工厂方法进行初始化
    private void initVew() {
        mViewDragHelper = ViewDragHelper.create(this, callback);
    }

    // 按顺序将子View分别定义成MenuView和MainView
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mMenuView = getChildAt(0);
        mMainView = getChildAt(1);
    }

    // 获得View的宽度
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = mMenuView.getMeasuredWidth();
    }

    // 第二步:重写拦截事件方法,将事件传递给ViewDragHelper方法
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 将触摸事件传递给ViewDragHelper,此操作必不可少
        mViewDragHelper.processTouchEvent(event);
        return true;
    }
// 第三步:处理回调Callback
    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
        // 判断何时开始检测触摸事件
        // 指定parentView中的哪一个子View可以被移动
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            // 如果当前触摸的child是mMainView时开始检测
            return mMainView == child;
        }

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

        // 当拖拽状态改变,比如idle,dragging
        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }

        // 当位置改变的时候调用,常用与滑动时更改scale等。Top代表在垂直方向上child移动的距离,
        // 而dy则表示比较前一次的增量。
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
        }

        // 处理垂直滑动
        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;
        }

        // 处理水平滑动
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;
        }

        // 拖动结束后调用(手指离开屏幕时的操作),方法内部是通过Scroller类来实现的,这是重写computeScroll()方法的原因
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            // 手指抬起后缓慢移动到指定位置
            if (mMainView.getLeft() < 350) {
                // 关闭菜单
                // 相当于Scroller的startScroll方法
                mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
                ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
            } else {
                // 打开菜单
                mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
                ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
            }
        }
    };

    // 第四步:实现滑动(模板代码)
    @Override
    public void computeScroll() {
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值