Android之ViewDragHelper的使用
关于ViewDragHelper
官方文档的介绍是:
ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.
这段话很容易理解,不重复啰嗦了。
实例
使用步骤:
1.使用ViewDragHelper的静态方法create创建,需要传人一个ViewDragHelper.CallBack对象;
2.实现ViewDragHelper.CallBack中的方法,来实现对视图的一些操作,如拖拽等
3.onInterceptTouchEvent和onTouchEvent回调ViewDragHelper中对应方法
下面通过实例来简单了解一下一些主要方法的使用:
- tryCaptureView
- clampViewPositionHorizontal
- clampViewPositionVertical
- onEdgeDragStarted
- onViewReleased
- getViewHorizontalDragRange/getViewVerticalDragRange
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<c.viewdraghpdemo.ViewDrag xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="c.viewdraghpdemo.MainActivity">
<TextView
android:layout_width="150dp"
android:layout_height="150dp"
android:text="Hello World!"
android:background="@android:color/holo_blue_dark"/>
</c.viewdraghpdemo.ViewDrag>
自定义的View
package c.viewdraghpdemo;
import android.content.Context;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* Created by Administrator on 2016/9/26.
*/
public class ViewDrag extends LinearLayout {
private static final String TAG = "hly";
private ViewDragHelper mViewDrag;
private TextView tv;
public ViewDrag(Context context) {
super(context, null);
}
public ViewDrag(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ViewDrag(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//Step1:使用静态方法构造ViewDragHelper,其中需要传入一个ViewDragHelper.Callback回调对象.
mViewDrag = ViewDragHelper.create(this, 1.0f, mViewDragCB);
//setEdgeTrackingEnabled设置的边界滑动时触发
mViewDrag.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
}
ViewDragHelper.Callback mViewDragCB = new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
//返回ture则表示可以捕获该view
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//手指触摸移动时实时回调, left就是child.getLeft()的值
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//同理
return top;
}
};
//重写onInterceptTouchEvent回调ViewDragHelper中对应的方法.
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDrag.shouldInterceptTouchEvent(ev);
}
//重写onTouchEvent回调ViewDragHelper中对应的方法.
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDrag.processTouchEvent(event);
return true;
}
//得到child实例
@Override
protected void onFinishInflate() {
tv = (TextView) getChildAt(0);
}
}
相关方法的用处已经在代码里注释了,应该很好理解
MainActivity.java
package c.viewdraghpdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
简单运行一下,看看效果:
可以看见现在TextView可以被拖动了,但是这里发现可以被拖到屏幕外面,这时候就要在ViewDragHelper.CallBack里面处理一下了,具体修改如下:
@Override
//手指触摸移动时实时回调, left就是child.getLeft()的值
public int clampViewPositionHorizontal(View child, int left, int dx) {
//可左右滑动的空间
int spaceX = getWidth() - getPaddingRight() - child.getWidth();
return Math.min(Math.max(getPaddingLeft(), left), spaceX);
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//同理
int spaceY = getHeight() - getPaddingBottom() - child.getHeight();
return Math.min(Math.max(getPaddingTop(), top), spaceY);
}
};
运行一下,发现不会移动到屏幕外了:
在上面的代码里面还有一句
mViewDrag.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
这个是滑动到边界的时候触发的,还需要在CallBack中实现一个方法
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
//当tryCaptureView为false时也可以生效
mViewDrag.captureChildView(tv, pointerId);
}
具体效果:
![这里写图片描述](https://img-blog.csdn.net/20160926173405240)
当我们释放手指时,想让子View滑到一个指定位置,可以用CallBack中的onViewReleased方法:
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (releasedChild == tv) {
mViewDrag.settleCapturedViewAt(300,300);
invalidate();
}
}
当onViewReleased中的invalidate()被调用之后,会不断调用调用computeScroll(),因此,要在ViewDrag类中重写一下这个方法:
@Override
public void computeScroll() {
if (mDragHelper.continueSettling(true)) {
invalidate();
}
}
运行效果:
当给tv设置了clickable为ture时,或者子view为Button时,发现根据上述的代码,拖拽子view是没反应的,原因是拖动的时候onInterceptTouchEvent方法,判断是否可以捕获,而在判断的过程中会去判断另外两个回调的方法getViewHorizontalDragRange和getViewVerticalDragRange,只有这两个方法返回大于0的值才能正常的捕获。如果未能正常捕获就会导致手势down后面的move以及up都没有进入到onTouchEvent。
解决方法:
@Override
public int getViewHorizontalDragRange(View child) {
return getMeasuredWidth() - child.getWidth();
}
@Override
public int getViewVerticalDragRange(View child) {
return getMeasuredHeight() - child.getHeight();
}
参考资料 :
http://blog.csdn.net/u012551350/article/details/51601985