View的拖动与碰撞检测
程序员文章站
2024-03-16 20:33:04
...
最近有用到拖动需求,正好学了下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
}
}
好了,基本上就是以上逻辑代码,如果有不妥之处还望不吝指教~~
推荐阅读
-
View的拖动与碰撞检测
-
Android实现跟随手指拖动并自动贴边的View样式(实例demo)
-
Android自定义控件:图形报表的实现(折线图、曲线图、动态曲线图)(View与SurfaceView分别实现图表控件)
-
一个简单的时钟View(关键点:Canvas的平移与旋转)
-
安卓三种动画之一View Animation与 案例 为活动的载入与退出添加动画
-
Activity的跳转以及Activity与layout,view的关系
-
jQuery与vue做出拖动验证的验证码效果
-
Android中ACTION_CANCEL的触发机制与滑出子view的情况
-
Android实现单页面浮层可拖动view的一种方法
-
Android实现单页面浮层可拖动view的示例代码