ViewDragHelper
使用ViewDragHelper可以轻松的实现拖动效果
简单Demo
XML中定义
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.jacob.viewdraghelper.tianrui.Practice1View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<TextView
android:layout_width="60dp"
android:layout_height="60dp"
android:background="#00ff00"/>
</com.jacob.viewdraghelper.tianrui.Practice1View>
</RelativeLayout>
ViewDragHelper的编写
在onInterceptTouchEvent()和onTouchEvent()中使用ViewDragHelper接受事件
@Override
public boolean
onInterceptTouchEvent(MotionEvent
event) {
switch (
event.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mViewDragHelper.cancel();
break;
}
return mViewDragHelper.shouldInterceptTouchEvent(
event);
}
@Override
public boolean
onTouchEvent(MotionEvent
event) {
mViewDragHelper.processTouchEvent(
event);
return true;
}
这些都是模板代码,要使用ViewDragHelper的话必须这么做.
编写CallBack代码
ViewDragHelper将MotionEvent帮我们处理成CallBack,我们只需处理CallBack中的逻辑就可以了,不用编写重复的拖动代码了. 最基本的有三个回调方法,使用这三个回调方法,能够处理大多数拖拽逻辑了
/**
* ViewDragHelper必备的回调方法
*/
private class ViewDragCallBack extends ViewDragHelper.Callback {
/**
* 判断当前需要拖动哪个View
*/
@Override
public boolean tryCaptureView(View child,
int pointerId) {
Log.d(TAG,
"tryCaptureView: " + child.getClass().getSimpleName());
return child == mTvTarget;
}
/**
* 在水平方向上,对拖动的位置进行限制
*
* @param child 进行拖动的View
* @param left 水平方向上的移动距离
* @param dx 水平方向上的移动增量
* @return 实际需要移动的值
*/
@Override
public int clampViewPositionHorizontal(View child,
int left,
int dx) {
final int leftBound = getLeft() + getPaddingLeft();
if (left < leftBound) {
return leftBound;
}
final int rightBound = getRight() - mTvTarget.getWidth() - getPaddingRight();
if (left > rightBound) {
return rightBound;
}
return left;
}
/**
* 处理竖直方向上的拖动
*/
@Override
public int clampViewPositionVertical(View child,
int top,
int dy) {
final int topBound = getTop() + getPaddingTop();
if (top < topBound) {
return topBound;
}
final int bottomBound = getBottom() - mTvTarget.getHeight() - getPaddingBottom();
if (top > bottomBound) {
return bottomBound;
}
return top;
}
}
注意事项:
如果第一子View不是TextView而是一个Button,那么就会出现无法拖动的情况,原因就是子View默认属性带有Clickable,会消耗掉这次事件序列,所以必须在OnInterceptTouchEvent()中进行拦截,使用ViewDragHelper中已经带了默认方法帮我们进行处理
/**
* 该方法用来描述View需要被拖动的距离,用此来适配子View具有Clickable属性的情况
*
* @param child 要拖动的View
* @return View在该方向上拖动的距离
*/
@Override
public int getViewHorizontalDragRange(View child) {
Log.d(TAG,
"getViewHorizontalDragRange: ");
return getMeasuredWidth() - child.getMeasuredWidth();
}
@Override
public int getViewVerticalDragRange(View child) {
return getMeasuredHeight() - child.getMeasuredHeight();
}
Demo1完整代码
/**
* Created by yangtianrui on 17-8-3.
* ViewDragHelper的基本使用
*/
public class Practice1View extends LinearLayout {
private static final String TAG =
"tianrui";
private TextView mTvTarget;
private ViewDragHelper mViewDragHelper;
public Practice1View(Context context) {
this(context,
null);
}
public Practice1View(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,
0);
}
public Practice1View(Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
mViewDragHelper = ViewDragHelper.create(
this,
1F,
new ViewDragCallBack());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
Log.d(TAG,
"onFinishInflate: ");
mTvTarget = (TextView) getChildAt(
0);
if (mTvTarget ==
null) {
throw new IllegalStateException(
"first child must be TextView.");
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mViewDragHelper.cancel();
break;
}
return mViewDragHelper.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
/**
* ViewDragHelper必备的回调方法
*/
private class ViewDragCallBack extends ViewDragHelper.Callback {
/**
* 判断当前需要拖动哪个View
*/
@Override
public boolean tryCaptureView(View child,
int pointerId) {
Log.d(TAG,
"tryCaptureView: " + child.getClass().getSimpleName());
return child == mTvTarget;
}
/**
* 在水平方向上,对拖动的位置进行限制
*
* @param child 进行拖动的View
* @param left 水平方向上的移动距离
* @param dx 水平方向上的移动增量
* @return 实际需要移动的值
*/
@Override
public int clampViewPositionHorizontal(View child,
int left,
int dx) {
final int leftBound = getLeft() + getPaddingLeft();
if (left < leftBound) {
return leftBound;
}
final int rightBound = getRight() - mTvTarget.getWidth() - getPaddingRight();
if (left > rightBound) {
return rightBound;
}
return left;
}
/**
* 处理竖直方向上的拖动
*/
@Override
public int clampViewPositionVertical(View child,
int top,
int dy) {
final int topBound = getTop() + getPaddingTop();
if (top < topBound) {
return topBound;
}
final int bottomBound = getBottom() - mTvTarget.getHeight() - getPaddingBottom();
if (top > bottomBound) {
return bottomBound;
}
return top;
}
/**
* 该方法用来描述View需要被拖动的距离,用此来适配子View具有Clickable属性的情况
*
* @param child 要拖动的View
* @return View在该方向上拖动的距离
*/
@Override
public int getViewHorizontalDragRange(View child) {
Log.d(TAG,
"getViewHorizontalDragRange: ");
return getMeasuredWidth() - child.getMeasuredWidth();
}
@Override
public int getViewVerticalDragRange(View child) {
return getMeasuredHeight() - child.getMeasuredHeight();
}
}
@Override
public void computeScroll() {
super.computeScroll();
if (mViewDragHelper.continueSettling(
true)) {
ViewCompat.postInvalidateOnAnimation(
this);
}
}
}
Demo2 使用ViewDragHelper实现惯性滑动
该方法的重写方式如下:
@Override
public void computeScroll() {
super.computeScroll();
if (mViewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
在onViewRelease()中实现惯性滑动逻辑
使用 mViewDragHelper.settleCapturedViewAt( left, top)将一个View进行惯性滑动,同时记得调用invalidate()进行重绘
/**
* 手指抬起时回调,一般用于实现惯性滑动
*
* @param releasedChild 拖动的View
* @param xvel x方向的速度
* @param yvel y方向的速度
*/
@Override
public void onViewReleased(View releasedChild,
float xvel,
float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
final int top = releasedChild.getTop();
Log.d(TAG,
"onViewReleased: " + String.format(
"mTop=%d, mMiddle=%d, mBottom=%d,yVel=%f, height=%d", mTop, mMiddle, mBottom, yvel, releasedChild.getHeight()));
if (yvel >
0) {
if (top < mMiddle) {
mViewDragHelper.settleCapturedViewAt(
0, mMiddle);
}
else if (top < mBottom) {
mViewDragHelper.settleCapturedViewAt(
0, mBottom);
}
}
else if (yvel <
0) {
if (top < mMiddle) {
mViewDragHelper.settleCapturedViewAt(
0, mTop);
}
else if (top < mBottom) {
mViewDragHelper.settleCapturedViewAt(
0, mMiddle);
}
}
invalidate();
}
}
Demo2完整代码
/**
* Created by yangtianrui on 17-8-5.
* ViewDragHelper实现惯性滑动
*/
public class Practice2View extends LinearLayout {
private static final String TAG =
"tianrui";
private ViewDragHelper mViewDragHelper;
private View mTarget;
private int mTop;
private int mMiddle;
private int mBottom;
public Practice2View(Context context) {
this(context,
null);
}
public Practice2View(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,
0);
}
public Practice2View(Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
mViewDragHelper = ViewDragHelper.create(
this,
1F,
new ViewDragHelperCallBack());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTarget = getChildAt(
0);
}
@Override
protected void onLayout(
boolean changed,
int l,
int t,
int r,
int b) {
super.onLayout(changed, l, t, r, b);
mTop = getTop();
mMiddle = (getBottom() - getTop()) /
2 - mTarget.getMeasuredHeight();
mBottom = getBottom() - mTarget.getMeasuredHeight();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mViewDragHelper.cancel();
break;
}
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
super.computeScroll();
if (mViewDragHelper.continueSettling(
true)) {
ViewCompat.postInvalidateOnAnimation(
this);
}
}
private class ViewDragHelperCallBack extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View child,
int pointerId) {
return child == mTarget;
}
@Override
public int clampViewPositionVertical(View child,
int top,
int dy) {
if (top < getTop()) {
return getTop();
}
if (top > (getBottom() - child.getHeight())) {
return getBottom() - child.getHeight();
}
return top;
}
@Override
public int clampViewPositionHorizontal(View child,
int left,
int dx) {
if (left < getLeft()) {
return getLeft();
}
if (left > (getRight() - child.getWidth())) {
return getRight() - child.getWidth();
}
return left;
}
/**
* 手指抬起时回调,一般用于实现惯性滑动
*
* @param releasedChild 拖动的View
* @param xvel x方向的速度
* @param yvel y方向的速度
*/
@Override
public void onViewReleased(View releasedChild,
float xvel,
float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
final int top = releasedChild.getTop();
Log.d(TAG,
"onViewReleased: " + String.format(
"mTop=%d, mMiddle=%d, mBottom=%d,yVel=%f, height=%d", mTop, mMiddle, mBottom, yvel, releasedChild.getHeight()));
if (yvel >
0) {
if (top < mMiddle) {
mViewDragHelper.settleCapturedViewAt(
0, mMiddle);
}
else if (top < mBottom) {
mViewDragHelper.settleCapturedViewAt(
0, mBottom);
}
}
else if (yvel <
0) {
if (top < mMiddle) {
mViewDragHelper.settleCapturedViewAt(
0, mTop);
}
else if (top < mBottom) {
mViewDragHelper.settleCapturedViewAt(
0, mMiddle);
}
}
invalidate();
}
}
}
参考http://blog.csdn.net/lmj623565791/article/details/46858663