注:该笔记是整理学习鸿洋大神自定义View系列博客的部分知识点。
ViewDragHelper 可以用于自定义 ViewGroup 中子 View 的拖动等效果。
使用方式:
1.创建实例
在构造方法中调用ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback(){ });
创建实例时,需要传入三个参数,第一个为当前的ViewGroup,第二个为sensitivity主要用于设置touchSlop,可见传入越大,mTouchSlop的值就会越小。第三个参数就是Callback,在用户的触摸过程中会回调相关方法
helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));
2.设置触摸事件
@Override
public boolean onInterceptTouchEvent(MotionEvent event)
{
final int action = MotionEventCompat.getActionMasked(ev);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mDragHelper.cancel();
return false;
}
return mDragger.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
mDragger.processTouchEvent(event);
return true;
}
3.实现 Callback() 方法
new ViewDragHelper.Callback(){
@Override
public boolean tryCaptureView(View child, int pointerId){
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx){
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy){
return top;
}
}
方法解释:
tryCaptureView()
如果返回的是true,则表示在该 ViewGroup 下的所有子 View 都可以移动;如果返回的是某一个子 View 时,表示只能该 View 可以移动。
clampViewPositionHorizontal(),clampViewPositionVertical()
可以在该方法中对child移动的边界进行控制,left , top 分别为即将移动到的位置
onViewDragStateChanged()
当ViewDragHelper状态发生变化时回调(IDLE,DRAGGING,SETTING[自动滚动时])
onViewPositionChanged()
onViewCaptured()
当captureview被捕获时回调
onViewReleased()
手指释放时,回调方法
onEdgeTouched()
当触摸到边界时回调
onEdgeLock()
true的时候会锁住当前的边界,false则unLock
onEdgeDragStarted()
边界拖动时,回调方法
getOrderedChildIndex()
改变同一个坐标(x,y)去寻找captureView位置的方法。(具体在:findTopChildUnder方法中)
getViewHorizontalDragRange()
控制横向手势事件能否正常捕获,返回大于0,表示正常捕获
getViewVerticalDragRange()
控制横向手势事件能否正常捕获,返回大于0,表示正常捕获
实例:
public class VDHLayout extends LinearLayout {
private ViewDragHelper viewDragHelper;
private View mDragView1,mDragView2,mDragView3;
private Point point=new Point();
public VDHLayout(Context context) {
super(context);
}
public VDHLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
viewDragHelper=ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
//指定哪个子View可以移动
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child==mDragView1 || child==mDragView2;
}
//横向移动范围
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
int leftBound=getPaddingLeft();
int rightBound=getWidth()-mDragView1.getWidth()-leftBound;
int newLeft=Math.min(Math.max(left,leftBound),rightBound);
return newLeft;
}
//纵向移动范围
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
int topBound=getPaddingTop();
int bottomBound=getHeight()-mDragView1.getHeight()-topBound;
int newTop=Math.min(Math.max(top,topBound),bottomBound);
return newTop;
}
//手指释放时,回调方法
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if(releasedChild==mDragView2){
viewDragHelper.settleCapturedViewAt(point.x,point.y); //手指释放,自动返回初始位置
invalidate();
}
}
//边界拖动时,回调方法
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
viewDragHelper.captureChildView(mDragView3,pointerId);
}
/**
* getViewHorizontalDragRange(),getViewVerticalDragRange()
* 如果子 View 消耗事件(如子 View 是 Button),那么就会进入 onInterceptTouchEvent() ,判断是否可以捕获,
* 而在判断的过程中会去判断上面两个回调方法,若方法返回大于 0 时,表示可以正常捕获。
* 上面两个方法分别控制横向与纵向的手势事件能否正常捕获
*/
@Override
public int getViewHorizontalDragRange(View child) {
return getMeasuredWidth()-child.getMeasuredWidth();
}
@Override
public int getViewVerticalDragRange(View child) {
return getMeasuredHeight()-child.getMeasuredHeight();
}
});
//边界检测,边界拖动的必要条件
viewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);
}
//获取子View的ID
@Override
protected void onFinishInflate() {
mDragView1=findViewById(R.id.one);
mDragView2=findViewById(R.id.two);
mDragView3=findViewById(R.id.three);
}
//事件分发
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return viewDragHelper.shouldInterceptTouchEvent(ev); //由ViewDragHelper 拦截事件
}
//处理事件
@Override
public boolean onTouchEvent(MotionEvent event) {
viewDragHelper.processTouchEvent(event); //ViewDragHelper 处理事件
return true;
}
//因为其内部使用的是mScroller.startScroll,所以别忘了需要invalidate()以及结合computeScroll方法一起
@Override
public void computeScroll() {
if(viewDragHelper.continueSettling(true)){
invalidate();
}
}
//Layout 中保存子 View 的初始位置,用于该 View 拖动后能自动回到原点
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
point.x=mDragView2.getLeft();
point.y=mDragView2.getTop();
}
}