View的拖动与碰撞检测

最近有用到拖动需求,正好学了下ViewDragHelper, 我的需求是这样,需要拖动一个View (以下简称DragView)然后检测碰撞,然后执行动画,这其中碰撞有分为 与在View上释放、View碰撞中 和 离开View ,此外目标碰撞View(以下简称TargetView)有可能包含多个,这里我用的是Rect的intersects函数,ViewDragHelper自带了一个isUnderView函数,但是因为我写的DragManager里面并没保存View而是ID,总之也是很轻易地实现了需求,DragHelper的code如下

public class DragHelper extends ViewDragHelper.Callback {
    private int mLeft;
    private int mTop;
    private Rect targetRect = new Rect();
    private Rect curRect = new Rect();
    boolean released = true;
    View targetView;
    @Override
    public boolean tryCaptureView(@NonNull View child, int pointerId) {
        //检测捕捉的View是否为DragView
        return child.getId() == ViewDragManager.getInstance().getDragViewId();
    }
    @Override
    public void onViewCaptured(@NonNull View capturedChild, int activePointerId) {
        super.onViewCaptured(capturedChild, activePointerId);
        mLeft = capturedChild.getLeft();
        mTop = capturedChild.getTop();
    }
  
    @Override
    public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
        super.onViewReleased(releasedChild, xvel, yvel);
        released = false;
        if (releasedChild.getId() == ViewDragManager.getInstance().getDragViewId()) {
            releasedChild.getHitRect(curRect);
            int flagHitTest = 0;
            for(int taregtIds :ViewDragManager.getInstance().getTargetViewIds()) {
                targetView = ((ViewGroup) releasedChild.getParent()).
                        findViewById(taregtIds);
                if(targetView == null){
                    continue;
                }
                targetView.getHitRect(targetRect);
                if (intersects(targetRect, curRect)) {
                    flagHitTest = 1;
                    ViewDragManager.getInstance().setHitViewId(taregtIds);
                    break;
                }
            }
            //如果动作释放,把DragView从当前检测列表中移除
            ViewDragManager.getInstance().removeTargetIds();
            if (flagHitTest == 1 && ViewDragManager.getInstance().getDraglistener() != null) {
                ViewDragManager.getInstance().getDraglistener().onReleaseTarget();
            }
            // 让释放的 DragView 停回初始位置
            ViewDragManager.getInstance().getViewDragHelper().settleCapturedViewAt(mLeft, mTop);
            ((View) releasedChild.getParent()).invalidate();
        }
    }

    @Override
    public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
        super.onViewPositionChanged(changedView, left, top, dx, dy);
        if (ViewDragManager.getInstance().getViewDragHelper().getViewDragState() != STATE_DRAGGING) {
            return;
        }
        changedView.getHitRect(curRect);
        int flagHitTest = 0;
        for(int taregtIds :ViewDragManager.getInstance().getTargetViewIds()) {
            targetView = ((ViewGroup) changedView.getParent()).
                    findViewById(taregtIds);
            if(targetView == null){
                continue;
            }
            targetView.getHitRect(targetRect);
            if (intersects(targetRect, curRect)) {
                flagHitTest = 1;
                ViewDragManager.getInstance().setHitViewId(taregtIds);
                break;
            }
        }
        if (ViewDragManager.getInstance().getDraglistener() != null) {
            ViewDragManager.getInstance().getDraglistener().onTouchTarget(flagHitTest == 1);
        }
    }
    @Override
    public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
        return left;
    }
    @Override
    public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
        return top;
    }

这里我因为是全局多个模块调用,所以临时写了个单例

public class ViewDragManager {
 private ViewDragHelper viewDragHelper;
    private IDraglistener draglistener;
    private int dragViewId;
    private int hitedViewId;
    private ArrayList<Integer> targetViewIds = new ArrayList<>(3);
    static ViewDragManager INSTANCE = new ViewDragManager();
    public static ViewDragManager getInstance() {
        return INSTANCE;
    }
    public ViewDragHelper getViewDragHelper() {
        return viewDragHelper;
    }
    public void setViewDragHelper(ViewDragHelper viewDragHelper) {
        this.viewDragHelper = viewDragHelper;
    }
    public IDraglistener getDraglistener() {
        return draglistener;
    }
    public void setDraglistener(IDraglistener draglistener) {
        this.draglistener = draglistener;
    }
    public int getDragViewId() {
        return dragViewId;
    }
    public void setDragViewId(int dragViewId) {
        this.dragViewId = dragViewId;
    }
    public ArrayList<Integer> getTargetViewIds() {
        return this.targetViewIds;
    }
    public void addTargetViewId(int targetViewId) {
        this.targetViewIds.add(targetViewId);
    }
    public int getHitedViewId() {
        return hitedViewId;
    }
    public void setHitViewId(int hitedViewId) {
        this.hitedViewId = hitedViewId;
    }
    public void clearHitViewId() {
        this.targetViewIds.clear();
    }
    public void removeTargetIds() {
        if (targetViewIds != null) {
            int idx = targetViewIds.indexOf(hitedViewId);
            if (idx >= 0) {
                targetViewIds.remove(idx);
            }
        }
    }

最后因为我的场景包含了一个HorizontalScrollView,但是在拖动时不能与它冲突,所以需要自定义个所有View的容器

public class DraggableConstraintLay extends ConstraintLayout {
    private ViewDragHelper viewDragHelper;
    public DraggableConstraintLay(Context context) {
        this(context,null);
    }

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

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

    public void init(){
        DragHelper dragHelper = new DragHelper();
        viewDragHelper = ViewDragHelper.create(this,dragHelper);
        ViewDragManager.getInstance().setViewDragHelper(viewDragHelper);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return viewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        viewDragHelper.processTouchEvent(event);
        switch (event.getAction()){
            case MotionEvent.ACTION_MOVE:
                if(viewDragHelper.getViewDragState() == STATE_DRAGGING) {
                   (getParent().getParent()).requestDisallowInterceptTouchEvent(true);
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return true;
    }


    @Override
    public void computeScroll() {
        if (viewDragHelper != null && viewDragHelper.continueSettling(true)) {
            invalidate();
        }
    }
}

使用的话就是根部局用DraggableConstraintLay,注意容器里面的View都得设定ID,不然编译会报错
然后写一个接口用于在UI中回调

public interface IDragListener {
    void onReleaseTarget();
    void onTouchTarget(boolean isHover);
}
public class MyActivity implements IDragListener{
     init(){
        ViewDragManager.getInstance().setDraglistener(this);
     }

    @Override
    public void onReleaseTarget() {
        //DragView释放的逻辑
    }

    @Override
    public void onTouchTarget(boolean isHover) {
       //DragView悬停 或  离开  TargetView
    }
}

好了,基本上就是以上逻辑代码,如果有不妥之处还望不吝指教~~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值