请选择 进入手机版 | 继续访问电脑版

12360技术网 - 专业IT技术发表平台

 立即注册  找回密码
查看: 7769|回复: 3

ViewDragHelper实现简单的view拖拽和吸边功能

[复制链接]

22

主题

26

帖子

160

积分

注册会员

Rank: 2

积分
160
发表于 2020-1-27 10:30:25 | 显示全部楼层 |阅读模式
工作了几年,最开始做的是安卓开发,后面的做了一段时间逆向和sdk开发,一直没有系统的整理自己的知识,打算从本篇博客开始,陆续复习并记录一下自己的安卓知识

一直不知道怎么排版,先凑合着弄下
实现效果,gif上传被压扁了

 
ViewDragHelper的用法

viewDragHelper是一个安卓自带的处理拖拽的工具
先看一下viewDragHelper的创建步骤
  1. public static ViewDragHelper create(@NonNull ViewGroup forParent, @NonNull ViewDragHelper.Callback cb)
复制代码
ViewGroup传的就是需要操作的View容器,一般我们把代码写在自定义View内,这里也就直接传this
ViewDragHelper.Callback 这个是处理拖动逻辑的核心模块,具体的方法有
  1. public boolean tryCaptureView(@NonNull View view, int i)
复制代码
这是判定规则,只有return true的时候才会去执行后续的拖动操作
这里的view是容器内被touch到的字view,只有这个view和我们需要拖动的view为同一个的时候我们才认为是匹配的
也就是 return  myView == view;
  1. public int getViewHorizontalDragRange(@NonNull View child);public int getViewVerticalDragRange(@NonNull View child);
复制代码
容器内可以拖动的区间,只有大于0的时候才可以执行相应方向的操作,一般没有特殊要求,我们会把这个返回值设为当期容器的宽和高
  1. public int clampViewPositionHorizontal(@NonNull View child, int left, int dx); public int clampViewPositionVertical(@NonNull View child, int top, int dy);
复制代码
这个返回的是被操作的view在横向或纵向所能滑出的最大距离,或者说,在x或y方向的最左或最右,最上或最下所能达到的位置,这个可以有正负,比如x方向负就直接超出左边屏幕了哈
  1. public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel);
复制代码
这个是view被释放时候执行的操作,我们可以操作view回到指定的位置或者保持不变等
在介绍ViewDragHelper的部分方法
  1. public boolean settleCapturedViewAt(int finalLeft, int finalTop)
复制代码
把我们所操作的view平滑的滑动到指定的位置
  1. public boolean smoothSlideViewTo(@NonNull View child, int finalLeft, int finalTop)
复制代码
和上面的settleCapturedViewAt效果类似,这个可以传被滑动的指定view
但是单纯用这两个方法会发现view并没有变动,这就需要搭配下面的方法一起使用
  1. public boolean continueSettling(boolean deferCallbacks);public boolean shouldInterceptTouchEvent(@NonNull MotionEvent ev);public void processTouchEvent(@NonNull MotionEvent ev);
复制代码
拖动处理三件套,需要让viewDragHelper处理手势和scroller圆滑过渡
实现
首先我们定义一些扩展的变量
  1. private View dragView;                //被拖拽的viewprivate ViewDragHelper viewDragHelper;private int mWidth;                 //容器的宽度private int mHeight;                //容器的高度private int mChildWidth;            //拖拽的View宽度private int mChildHeight;           //拖拽的View高度private boolean onDrag = true;      //是否正在被拖拽private boolean dragEnable = true;  //是否是可以拖拽的private boolean sideEnable = true;  //是否吸边private final int NONE = -1;private int topFinalOffset;        //拖拽超出上边界后,释放后回到的top位置private int bottomFinalOffset;     //拖拽超出下边界后,释放后回到的bottom位置private int leftFinalOffset;       //拖拽超出左边界后,释放后回到的left位置private int rightFinalOffset;      //拖拽超出右边界后,释放后回到的right位置    private int leftDragOffset = NONE;            //能向左拖拽的最大距离private int rightDragOffset = NONE;            //能向右拖拽的最大距离private int topDragOffset = NONE;             //能向上拖拽的最大距离private int bottomDragOffset = NONE;             //能向下拖拽的最大距离
复制代码
先初始化并获取一些参数
  1. private void init() {        viewDragHelper = ViewDragHelper.create(this, new MyDragCallBack());} @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        //获取装载容器的宽高以及拖拽view的宽高        mWidth = getMeasuredWidth();        mHeight = getMeasuredHeight();        mChildHeight = dragView.getMeasuredHeight();        mChildWidth = dragView.getMeasuredWidth();        //默认最多可以拖拽1/2的view出屏幕        leftDragOffset = leftDragOffset == NONE ? mChildWidth / 2 : leftDragOffset;        rightDragOffset = rightDragOffset == NONE ? mChildWidth / 2 : rightDragOffset;        topDragOffset = topDragOffset == NONE ? mChildHeight / 2 : topDragOffset;        bottomDragOffset = bottomDragOffset == NONE ? mChildHeight / 2 : bottomDragOffset;    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        if (lastChildX == 0 && lastChildY == 0) {            calLayoutOffset();        }        //把view布局到相应的位置,当然第一次就是在左上角,后续位置会发生变化        dragView.layout(lastChildX, lastChildY, lastChildX + mChildWidth, lastChildY + mChildHeight);    }    public void calLayoutOffset() {        //把x,y初始化设置为最终要停留在左上角的位置        lastChildX =leftFinalOffset;        lastChildY =topFinalOffset;    }@Override    protected void onFinishInflate() {        super.onFinishInflate();        if (getChildCount() != 1) {            throw new RuntimeException("child size must be 1");        }        dragView = getChildAt(0);        dragView.bringToFront();    }
复制代码
  1. private Rect mRect = new Rect();    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        if (dragEnable) {            switch (ev.getAction()) {                case MotionEvent.ACTION_DOWN:                    int x = (int) ev.getX();                    int y = (int) ev.getY();                    dragView.getHitRect(mRect);                    onDrag = mRect.contains(x, y);                    //如果按下的点在dragView内,则认为是拖动有效,执行viewDragHelper的方法                    break;            }            if (onDrag) return viewDragHelper.shouldInterceptTouchEvent(ev);        }        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        if (dragEnable) {            if (onDrag) {                viewDragHelper.processTouchEvent(event);                return true;            }        }        return super.onTouchEvent(event);    }    @Override    public void computeScroll() {        if (dragEnable) {            if (viewDragHelper.continueSettling(true)) {                invalidate();            }        }    }    public static int dp2px(Context context, float dpVal) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                d
复制代码
准备做完了,那么就要进行操作的代码了
[code] private class MyDragCallBack extends ViewDragHelper.Callback {        @Override        public boolean tryCaptureView(@NonNull View view, int i) {            return dragView == view;        }        //以横向拖动为例        //left是当前拖动view的左边的坐标        //我们要做的就是让 left >= 最左的距离  同时  left  mChildWidth ? mChildWidth : leftDragOffset;            rightDragOffset = rightDragOffset > mChildWidth ? mChildWidth : rightDragOffset;            return clamp(left, -leftDragOffset, mWidth - mChildWidth + rightDragOffset);        }        @Override        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {            topDragOffset = topDragOffset > mChildHeight ? mChildHeight : topDragOffset;            bottomDragOffset = bottomDragOffset > mChildHeight ? mChildHeight : bottomDragOffset;            return clamp(top, -topDragOffset, mHeight - mChildHeight + bottomDragOffset);        }        @Override        public int getViewVerticalDragRange(@NonNull View child) {//            return super.getViewVerticalDragRange(child);            return mHeight;        }        @Override        public int getViewHorizontalDragRange(@NonNull View child) {//            return super.getViewHorizontalDragRange(child);            return mWidth;        }        @Override        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {            if (sideEnable) {                super.onViewReleased(releasedChild, xvel, yvel);            //如果top小于topFinOffset则取topFinalOffset            //如果bottom大于最大的offset则取限制的最大bottom                int finalTop = dragView.getTop() = mHeight - bottomFinalOffset ? mHeight - dragView.getMeasuredHeight() - bottomFinalOffset : dragView.getTop();                lastChildY = finalTop;                //根据left和view的一半进行界定,选择是最终停留在左边还是右边                if (Math.abs(dragView.getLeft())  mChildWidth ? mChildWidth : leftDragOffset;            rightDragOffset = rightDragOffset > mChildWidth ? mChildWidth : rightDragOffset;            return clamp(left, -leftDragOffset, mWidth - mChildWidth + rightDragOffset);        }        @Override        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {            topDragOffset = topDragOffset > mChildHeight ? mChildHeight : topDragOffset;            bottomDragOffset = bottomDragOffset > mChildHeight ? mChildHeight : bottomDragOffset;            return clamp(top, -topDragOffset, mHeight - mChildHeight + bottomDragOffset);        }        @Override        public int getViewVerticalDragRange(@NonNull View child) {//            return super.getViewVerticalDragRange(child);            return mHeight;        }        @Override        public int getViewHorizontalDragRange(@NonNull View child) {//            return super.getViewHorizontalDragRange(child);            return mWidth;        }        @Override        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {            if (sideEnable) {                super.onViewReleased(releasedChild, xvel, yvel);                int finalTop = dragView.getTop() = mHeight - bottomFinalOffset ? mHeight - dragView.getMeasuredHeight() - bottomFinalOffset : dragView.getTop();                lastChildY = finalTop;                if (Math.abs(dragView.getLeft())




上一篇:Android与服务器数据交互实现用户登录
回复

使用道具 举报

13

主题

20

帖子

196

积分

注册会员

Rank: 2

积分
196
发表于 2020-2-6 13:49:35 | 显示全部楼层
感谢楼主的无私分享![www.12360.co]
回复

使用道具 举报

0

主题

25

帖子

535

积分

高级会员

Rank: 4

积分
535
发表于 2020-2-11 00:42:07 | 显示全部楼层
其实我一直觉得楼主的品味不错!呵呵![www.12360.co]
回复

使用道具 举报

0

主题

9

帖子

199

积分

注册会员

Rank: 2

积分
199
发表于 4 天前 | 显示全部楼层
既然你诚信诚意的推荐了,那我就勉为其难的看看吧![www.12360.co]
回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

12360技术网

GMT+8, 2020-2-22 11:19 , Processed in 0.093641 second(s), 40 queries .

本网站内容收集于互联网,Www.12360.Co不承担任何由于内容的合法性及健康性所引起的争议和法律责任。 欢迎大家对网站内容侵犯版权等不合法和不健康行为进行监督和举报。

© 2019-2020 Www.12360.Co

快速回复 返回顶部 返回列表