Android 实现View滑动几种方式

关于View的滑动,Android中提供了许多方法,具体可以分为一下几类:

layout

在ACTION_MOVE中通过获取x、y的偏移量动态布局view,并禁止向父控件传递事件:

@Override
    public boolean onTouchEvent(MotionEvent event) {

        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                break;

            case MotionEvent.ACTION_MOVE:
                int offsetX = x - mLastX;
                int offsetY = y - mLastY;

                layout(
                        getLeft() + offsetX,
                        getTop() + offsetY,
                        getRight() + offsetX,
                        getBottom() + offsetY
                );
                break;
        }

        return true;
    }

offsetLeftAndRight和offsetTopAndBottom

将上述layout方法改为该两个方法的组合,偏移量计算同上:

 case MotionEvent.ACTION_MOVE:
                int offsetX = x - mLastX;
                int offsetY = y - mLastY;

                offsetLeftAndRight(offsetX);
                offsetTopAndBottom(offsetY);
                break;

LayoutParams

实现方法和layout相似,动态设置布局参数,缺点是如果view参数加了rules,会导致无法滑动:

    case MotionEvent.ACTION_MOVE:
                int offsetX = x - mLastX;
                int offsetY = y - mLastY;

                ViewGroup.MarginLayoutParams params
                        = (ViewGroup.MarginLayoutParams) getLayoutParams();

                params.leftMargin = getLeft() + offsetX;
                params.topMargin = getTop() + offsetY;

                setLayoutParams(params);
                break;

ScrollBy

scrollBy移动的是view中的内容或者ViewGroup中的所有子view,故需先获得父控件的实例;同时scrollBy参数的正负是和android坐标轴的正负方向相反的,故需队offset值取反:

 case MotionEvent.ACTION_MOVE:
                int offsetX = x - mLastX;
                int offsetY = y - mLastY;

                ((View) getParent()).scrollBy(-offsetX, -offsetY);
                break;

ViewDragHelper

该类非常强大,基本可以各种不同的滑动、拖放需求。support库中DragLayout也是基于此类实现侧滑效果。使用该类一般需要如下几个步骤:

  • 继承自ViewGroup,在构造中初始化ViewDragHelper对象,传入的两个参数分别为当前容器对象和ViewDragHelper中的回调接口,该回调中封装了子view的滑动操作相关api
    public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mViewDragHelper = ViewDragHelper.create(this, mCallBack);
    }
  • 触摸事件拦截与处理,需要将事件传递给ViewDragHelper处理
 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }
  • 处理ViewDragHelper中回调Callback
private ViewDragHelper.Callback mCallBack = new ViewDragHelper.Callback() {

        /**
         * 指定可以滑动的子View,比如当前只有mMainView可以滑动
         * @param child
         * @param pointerId
         * @return true 指定的子view可以滑动
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == mMainView;
        }

        /**
         * 控制子view水平滑动的距离,默认是0 不可以滑动
         * @param child
         * @param left
         * @param dx
         * @return
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;
        }

        /**
         * 功能同上,垂直方向滑动的距离
         * @param child
         * @param top
         * @param dy
         * @return
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return top;
        }

        /**
         * 当拖拽子view之后手指离开屏幕时触发;
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {

        }
    };

以上代码即可实现view滑动的功能。但如果想要实现手指离开屏幕view自动滑动到指定位置的功能时,需要在Callback中重写onViewReleased()方法。ViewDragHelper提供了smoothSlideViewTo() 方法,可以实现view的自动滑动效果,当然其内部也是通过Scroller类实现的,故需要重写computeScroll() 方法。

如下是一个模拟google DragLayout的一个Demo,当然只实现了其中侧滑的一个小功能,其他事件分发处理逻辑都没有具体实现。

package com.ts.test.widget;

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;

/**
 * Created by tiansen on 17-11-18.
 */

public class MyDragLayout extends FrameLayout {

    private static final String TAG = "MyDragLayout";
    private ViewDragHelper mViewDragHelper;

    /**
     * 侧边栏View
     */
    private View mMenuView;

    /**
     * 主View
     */
    private View mMainView;

    /**
     * 侧边栏View宽度
     */
    private int mMenuWidth;

    /**
     * 主View宽度
     */
    private int mMainWidth;

    private ViewDragHelper.Callback mCallBack = new ViewDragHelper.Callback() {

        /**
         * 指定可以滑动的子View
         * @param child
         * @param pointerId
         * @return true 指定的子view可以滑动
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == mMainView;
        }

        /**
         * 控制子view水平滑动的距离,默认是0 不可以滑动
         * @param child
         * @param left
         * @param dx
         * @return
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            // 控制滑动边界
            return (left < 0 ? 0 : left) < mMenuWidth ? left : mMenuWidth;
        }

        /**
         * 功能同上,垂直方向滑动的距离
         * @param child
         * @param top
         * @param dy
         * @return
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;
        }

        /**
         * 当拖拽子view之后手指离开屏幕时触发;
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if (mMainView.getLeft() < mMenuWidth / 2) {
                mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
                ViewCompat.postInvalidateOnAnimation(MyDragLayout.this);
            } else {
                mViewDragHelper.smoothSlideViewTo(mMainView, mMenuWidth, 0);
                ViewCompat.postInvalidateOnAnimation(MyDragLayout.this);
            }
        }
    };

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

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

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

        mViewDragHelper = ViewDragHelper.create(this, mCallBack);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mMenuView = getChildAt(0);
        mMainView = getChildAt(1);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mMainWidth = mMainView.getMeasuredWidth();
        mMenuWidth = mMenuView.getMeasuredWidth();
    }


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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

    @Override
    public void computeScroll() {
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值