最近做了个移动拼图游戏,里面用到ViewDragHelper 去实现子VIEW的控制,感觉这个父布局控件可以复用,记录下来,以供以后可以能需要。
主要使用了ViewDragHelper 管理子VIEW的移动,另外封装了几个接口提供对外,基本上算是个完整的DEMO
package com.ubt.movedview; import android.content.Context; import android.graphics.Point; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.widget.ViewDragHelper; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.RelativeLayout; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @描述: 可以随意移动子VIEW的布局 */ public class MovedView extends RelativeLayout { private final String TAG = "MovedView"; //处理子VIEW滑动的帮助类 private ViewDragHelper mDragger; private DragHelperCallback mCallback; //需要滑动的view都需要添加到这个列表中 private List<View> mNeedMovedView = new ArrayList<>(); private Map<View, Point> mSrcPoint = new HashMap<>(); //是否已经获取了子VIEW的初始坐标 private boolean hasGetSrcPoint; //控制子VIEW 的回调接口 private IMoveCallback mMoveCallback; public MovedView(@NonNull Context context) { this(context, null); } public MovedView(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MovedView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mCallback = new DragHelperCallback(); mDragger = ViewDragHelper.create(this, 1.0f, mCallback); } public class DragHelperCallback extends ViewDragHelper.Callback{ @Override public boolean tryCaptureView(View child, int pointerId) { return mNeedMovedView.contains(child); } @Override public void onViewCaptured(View capturedChild, int activePointerId) { super.onViewCaptured(capturedChild, activePointerId); } @Override public int clampViewPositionHorizontal(View child, int left, int dx){ final int leftBound = getPaddingLeft(); final int rightBound = getWidth() - child.getWidth() - leftBound; final int newLeft = Math.min(Math.max(left, leftBound), rightBound); return newLeft; } @Override public int clampViewPositionVertical(View child, int top, int dy){ final int topBound = getPaddingTop(); final int bottomBound = getHeight() - child.getHeight() - topBound; final int newTop = Math.min(Math.max(top, topBound), bottomBound); return newTop; } @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); // Log.i(TAG,"onViewPositionChanged left=="+left+" top="+top); if(mMoveCallback != null){ mMoveCallback.onViewPosition(changedView, left, top, dx, dy); } } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); if(mNeedMovedView.contains(releasedChild)) { if(mMoveCallback != null) { Point srcPosition = mMoveCallback.getDestPositon(releasedChild); if(srcPosition != null) { mDragger.settleCapturedViewAt(srcPosition.x, srcPosition.y); invalidate(); } } } } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mDragger.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { mDragger.processTouchEvent(event); return true; } @Override public void computeScroll(){ if(mDragger.continueSettling(true)) { invalidate(); } } @Override protected void onFinishInflate() { super.onFinishInflate(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if(!hasGetSrcPoint) { hasGetSrcPoint = true; if(mMoveCallback != null){ mMoveCallback.onLayoutFinish(); } } } /** * 把需要移动的VIEW添加进父布局 * @param view 需要移动的VIEW */ public void addNeedMovedView(@NonNull View view){ mNeedMovedView.add(view); } /** * 移出后子VIEW不再可以移动 * @param view 需要移出的VIEW */ public void removeNeedMovedView(@NonNull View view){ mNeedMovedView.remove(view); mSrcPoint.remove(view); } /** * 添加控制子VIEW的回调接口 * @param moveCallback */ public void setMoveCallback(IMoveCallback moveCallback){ mMoveCallback = moveCallback; } public void removeMoveCallback(){ mMoveCallback = null; } public interface IMoveCallback{ //布局完成回调 void onLayoutFinish(); //当前移动子VIEW的坐标 void onViewPosition(View changedView, int left, int top, int dx, int dy); //返回指定VIEW再释放移动后,应该回到的坐标 Point getDestPositon(View view); } }