上一篇学习了 android里可以进行scroll 的 4 个方法,还有一个重要的 ViewDragHelper ,在这里重点学习一下。
首先我们先把这个ViewDragHelper 的框架先搭建起来,让它里的View 可以随意拖动。
首先自定义一个View 继承 ViewGroup ,先来一个完成代码,后面学习细节
package mw.huawei.com.viewdraghelper.MyView;
import android.content.Context;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
/**
* Created by Administrator on 2018/5/6.
*/
public class ViewDragHelperView extends LinearLayout {
private View mDragView; //随意拖动View
private View mAutoBackView ; // 拖动释放后返回原位置
private View mEdgeTrackerView ; // 从边缘触发拖动
private ViewDragHelper mViewDragHelper;
private int mAutoBackViewX;
private int mAutoBackViewY;
public ViewDragHelperView(@NonNull Context context) {
this(context,null);
}
public ViewDragHelperView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public ViewDragHelperView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
// STEP 1:初始 mViewDragHelper
private void initView(){
mViewDragHelper = ViewDragHelper.create(this,1.0f,callback);
mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); // 左边缘拖动 调用 onEdgeDragStarted 里传入的View
}
// SETP 2:拿到回调参数callBack
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
@Override //哪一个View可以被拖动
public boolean tryCaptureView(View child, int pointerId) {
// mEdgeTrackerView 从边缘触发,禁止拖动
return mDragView == child || mAutoBackView == child;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
int rangeX = getWidth() - child.getWidth();
if (left <= 0){
return 0;
}else if (left > rangeX){
return rangeX;
}else{
return left;
}
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
int rangeY = getHeight() - child.getHeight();
if (top<=0){
return 0;
}else if (top > rangeY){
return rangeY;
}else{
return top;
}
}
//手指释放的时候回调
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if(releasedChild == mAutoBackView){
mViewDragHelper.settleCapturedViewAt(mAutoBackViewX,mAutoBackViewY);//传入原始的位置左边,让mAutoBackView返回原位置
}
invalidate();// 如果没有执行完毕, 就执行computeScroll,继续计算
}
//在边界拖动时回调
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
mViewDragHelper.captureChildView(mEdgeTrackerView, pointerId);
}
};
//STEP 3:将View 的触摸 和 事件拦截都扔给 mViewDragHelper
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
//固定写法
//此方法用于自动滚动,比如自动回滚到默认位置.
if (mViewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mDragView=getChildAt(0);
mAutoBackView=getChildAt(1);
mEdgeTrackerView=getChildAt(2);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mAutoBackViewX = mAutoBackView.getLeft();
mAutoBackViewY = mAutoBackView.getTop();
}
}
现在分为三步实现这个ViewDragHelper
// STEP 1:初始 mViewDragHelper
// STEP 1:初始 mViewDragHelper
private void initView(){
mViewDragHelper = ViewDragHelper.create(this,1.0f,callback);
mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); // 左边缘拖动 调用 onEdgeDragStarted 里传入的View
}
// SETP 2:拿到回调参数callBack
// SETP 2:拿到回调参数callBack
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
@Override //哪一个View可以被拖动
public boolean tryCaptureView(View child, int pointerId) {
// mEdgeTrackerView 从边缘触发,禁止拖动
return mDragView == child || mAutoBackView == child;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
int rangeX = getWidth() - child.getWidth();
if (left <= 0){
return 0;
}else if (left > rangeX){
return rangeX;
}else{
return left;
}
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
int rangeY = getHeight() - child.getHeight();
if (top<=0){
return 0;
}else if (top > rangeY){
return rangeY;
}else{
return top;
}
}
//手指释放的时候回调
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if(releasedChild == mAutoBackView){
mViewDragHelper.settleCapturedViewAt(mAutoBackViewX,mAutoBackViewY);//传入原始的位置左边,让mAutoBackView返回原位置
}
invalidate();// 如果没有执行完毕, 就执行computeScroll,继续计算
}
//在边界拖动时回调
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
mViewDragHelper.captureChildView(mEdgeTrackerView, pointerId);
}
};
//STEP 3:将View 的触摸 和 事件拦截都扔给 mViewDragHelper
//STEP 3:将View 的触摸 和 事件拦截都扔给 mViewDragHelper
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
到这里就可以实现示例图中第一个方块的拖动效果啦。
下来在进阶一下下,实现第二个小方块的操作
拖动一个View 然后返回某一个坐标位置,需要重写如下方法,就只能简单的拖动了
@Override
public void computeScroll() {
//固定写法
//此方法用于自动滚动,比如自动回滚到默认位置.
if (mViewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
这个方法主要用来处理View进行平滑滑动。
并且需要重写onViewReleased()实现当手指松开的时候回到原位置的操作
//手指释放的时候回调
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if(releasedChild == mAutoBackView){
mViewDragHelper.settleCapturedViewAt(mAutoBackViewX,mAutoBackViewY);//传入原始的位置左边,让mAutoBackView返回原位置
}
invalidate();// 如果没有执行完毕, 就执行computeScroll,继续计算
}
当然这里传入的两个参数是在 自定义View Layout时候进行坐标的获取。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mAutoBackViewX = mAutoBackView.getLeft();
mAutoBackViewY = mAutoBackView.getTop();
}
然后实现边缘控制:如图中的第三个小方块,只能在左侧拖动进行控制。
主要实现callback 中 onEdgeDragStarted() 的操作
//在边界拖动时回调
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
mViewDragHelper.captureChildView(mEdgeTrackerView, pointerId);
}
传入的View就是我们实现的左侧边缘拖动控制的View
同时还需要在 mViewDragHelper 初始化的时候设置
mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
以实现左侧边缘控制。
activity_main.xml 如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mw.huawei.com.viewdraghelper.MainActivity">
<mw.huawei.com.viewdraghelper.MyView.ViewDragHelperView
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="match_parent">
<TextView
android:layout_margin="10dp"
android:gravity="center"
android:layout_gravity="center"
android:background="#88ff0000"
android:text="I can be dragged !"
android:layout_width="100dp"
android:layout_height="100dp"/>
<TextView
android:layout_margin="10dp"
android:layout_gravity="center"
android:gravity="center"
android:background="#8800ff00"
android:text="I will bakc to original loc !"
android:layout_width="100dp"
android:layout_height="100dp"/>
<TextView
android:layout_margin="10dp"
android:layout_gravity="center"
android:gravity="center"
android:background="#880000ff"
android:text=" edage to ctrl me"
android:layout_width="100dp"
android:layout_height="100dp"/>
</mw.huawei.com.viewdraghelper.MyView.ViewDragHelperView>
</RelativeLayout>
最后奉上我们的成果: