viewdraghelper的使用

使用viewdraghelper 来处理一些复杂的滑动事件



1.首先 自定义一个viewgroup

这里我们选择继承LinearLayout(可以自行选择)

2.调用ViewDragHelper.create(this, 1f, new DragHelper());来创建一个ViewDragHelper对象 

DragHelper是继承了ViewDragHelper.Callback 的内部类

 public class DragHelper extends ViewDragHelper.Callback {} 在这里重写一些方法来对事件进行处理


3.重写onInterceptTouchEvent (可选) onTouchEvent方法

<span style="font-size:14px;">@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		final int action = MotionEventCompat.getActionMasked(ev);
		if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
			mDragHelper.cancel();
			return false;
		}
		return mDragHelper.shouldInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		mDragHelper.processTouchEvent(ev);//必须加上这句话调用<span style="font-size:14px;">ViewDragHelper对象来处理触摸事件 否则不响应</span>
<span style="white-space:pre">	</span>
		return true;
	}</span>



完整代码

<span style="font-size:14px;">package com.flavienlaurent.vdh;

import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

/**
 * Created by Flavien Laurent (flavienlaurent.com) on 23/08/13.
 */
public class DragLayout extends LinearLayout {

	private final ViewDragHelper mDragHelper;

	private View mDragView1;
	private View mDragView2;

	private boolean mDragEdge;
	private boolean mDragHorizontal;
	private boolean mDragCapture;
	private boolean mDragVertical;

	public DragLayout(Context context) {
		this(context, null);
	}

	public DragLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public DragLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mDragHelper = ViewDragHelper.create(this, 1f, new DragHelper());
	}

	@Override
	protected void onFinishInflate() {
		mDragView1 = findViewById(R.id.drag1);
		mDragView2 = findViewById(R.id.drag2);
	}
	/**
	 * 设置只能横向移动
	 * @param dragHorizontal
	 */
	public void setDragHorizontal(boolean dragHorizontal) {
		mDragHorizontal = dragHorizontal;
		mDragView2.setVisibility(View.GONE);
	}
	/**
	 * 设置只能纵向移动
	 * @param dragHorizontal
	 */
	public void setDragVertical(boolean dragVertical) {
		mDragVertical = dragVertical;
		mDragView2.setVisibility(View.GONE);
	}

	public void setDragEdge(boolean dragEdge) {
		//滑动左边缘(以左边缘的坐标为基准)
		mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
		mDragEdge = dragEdge;
		mDragView2.setVisibility(View.GONE);
	}

	public void setDragCapture(boolean dragCapture) {
		mDragCapture = dragCapture;
	}

	 public class DragHelper extends ViewDragHelper.Callback {
		 /**
		  * <span style="color:#ff0000;">判断当前点击的view是否可以移动</span>
		  */
		@Override
		public boolean tryCaptureView(View child, int pointerId) {
			if (mDragCapture) {
				return child == mDragView1;
			}
			return true;
		}
		/**
		 * 位置移动时触发
		 */
		@Override
		public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
			invalidate();
		}
		
		@Override
		public void onViewCaptured(View capturedChild, int activePointerId) {
			super.onViewCaptured(capturedChild, activePointerId);
		}

		@Override
		public void onViewReleased(View releasedChild, float xvel, float yvel) {
			super.onViewReleased(releasedChild, xvel, yvel);
		}

		@Override
		public void onEdgeTouched(int edgeFlags, int pointerId) {
			super.onEdgeTouched(edgeFlags, pointerId);
		}

		@Override
		public void onEdgeDragStarted(int edgeFlags, int pointerId) {
			if (mDragEdge) {
				mDragHelper.captureChildView(mDragView1, pointerId);
			}
		}
		/**
		 * 处理纵向移动 </span><span style="color:#ff0000;font-size:14px;">必须重写</span><span style="font-size:14px;">
		 * @param top  当前操纵的view 的应该到达的top值
		 * @param dy 距离的绝对值
		 */
		@Override
		public int </span><span style="color:#ff0000;font-size:14px;">clampViewPositionVertical</span><span style="font-size:14px;">(View child, int top, int dy) {
			if (mDragVertical) {
				//当前视图离顶部的距离
				final int topBound = getPaddingTop();
				//手机高度-操纵的view的高度
				final int bottomBound = getHeight() - mDragView1.getHeight();
				//移动到的新高度
				final int newTop = Math.min(Math.max(top, topBound), bottomBound);

				return newTop;
			}
			return super.clampViewPositionVertical(child, top, dy);
//			return top;
		}
		/**
		 * 处理横向移动 </span><span style="font-size:12px;color:#ff0000;"><span style="font-family: Arial, Helvetica, sans-serif;">必须重写</span></span><span style="font-size:14px;">
		 * @param left  当前操纵的view 的应该到达的left值
		 * @param dx 距离的绝对值
		 */
		@Override
		public int </span><span style="color:#ff0000;font-size:14px;">clampViewPositionHorizontal</span><span style="font-size:14px;">(View child, int left, int dx) {
			if (mDragHorizontal || mDragCapture || mDragEdge) {
				final int leftBound = getPaddingLeft();
				final int rightBound = getWidth() - mDragView1.getWidth();

				final int newLeft = Math.min(Math.max(left, leftBound), rightBound);

				return newLeft;
			}
			return super.clampViewPositionHorizontal(child, left, dx);
		}

	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		final int action = MotionEventCompat.getActionMasked(ev);
		if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
			mDragHelper.cancel();
			return false;
		}
		return mDragHelper.shouldInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		<span style="color:#ff0000;">//!!!重要</span>
		mDragHelper.</span><span style="color:#ff0000;font-size:14px;">processTouchEvent</span><span style="font-size:14px;">(ev);
		return true;
	}

}
</span>



布局:

<com.flavienlaurent.vdh.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dragLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <View
        android:id="@+id/drag1"
        android:layout_width="96dp"
        android:layout_height="96dp"
        android:background="#AD78CC" />

    <View
        android:id="@+id/drag2"
        android:layout_width="96dp"
        android:layout_height="96dp"
        android:background="#FF00FF" />

</com.flavienlaurent.vdh.DragLayout>

实现如下效果:



代码中的关键点:

1.tryCaptureView返回了唯一可以被拖动的header view;

2.拖动范围drag range的计算是在onLayout中完成的;

3.注意在onInterceptTouchEvent和onTouchEvent中使用的ViewDragHelper的若干方法;

4.在computeScroll中使用continueSettling方法(因为ViewDragHelper使用了scroller) 

5.smoothSlideViewTo方法来完成拖动结束后的惯性操作。



package com.flavienlaurent.vdh;

import android.content.Context;
import android.support.v4.view.MotionEventCompat;
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.view.ViewGroup;

/**
 * Created by Flavien Laurent (flavienlaurent.com) on 23/08/13.
 */
public class YoutubeLayout extends ViewGroup {

	private final ViewDragHelper mDragHelper;

	private View mHeaderView;
	private View mDescView;

	private float mInitialMotionX;
	private float mInitialMotionY;

	private int mDragRange;
	private int mTop;
	private float mDragOffset;

	public YoutubeLayout(Context context) {
		this(context, null);
	}

	public YoutubeLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public YoutubeLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mDragHelper = ViewDragHelper.create(this, 1f, new DragHelperCallback());
	}

	@Override
	protected void onFinishInflate() {
		mHeaderView = findViewById(R.id.header);
		mDescView = findViewById(R.id.desc);
	}

	public void maximize() {
		smoothSlideTo(0f);
	}

	public void minimize() {
		smoothSlideTo(1f);
	}
	/**
	 * 进行平滑的滑动效果
	 * @param slideOffset
	 * @return
	 */
	boolean smoothSlideTo(float slideOffset) {
		final int topBound = getPaddingTop();
		int y = (int) (topBound + slideOffset * mDragRange);
		// 滑动到指定位置
		if (<span style="color:#ff0000;">mDragHelper.smoothSlideViewTo(mHeaderView, mHeaderView.getLeft(), y)</span>) {
			//内部调用scroller来进行滑动的效果
			<span style="color:#ff0000;">ViewCompat.postInvalidateOnAnimation(this);</span>
			return true;
		}
		return false;
	}

	private class DragHelperCallback extends ViewDragHelper.Callback {

		@Override
		public boolean tryCaptureView(View child, int pointerId) {
			return child == mHeaderView;
		}

		@Override
		public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
			mTop = top;

			mDragOffset = (float) top / mDragRange;
			//设置轴心 设置缩放比例
			mHeaderView.setPivotX(mHeaderView.getWidth());
			mHeaderView.setPivotY(mHeaderView.getHeight());
			mHeaderView.setScaleX(1 - mDragOffset / 2);
			mHeaderView.setScaleY(1 - mDragOffset / 2);

			mDescView.setAlpha(1 - mDragOffset);

			
			//两者效果相同 requestLayout最终也是调用layout方法进行滑动效果的
//			mDescView.<span style="color:#ff0000;">layout</span>(left, mTop + mHeaderView.getMeasuredHeight(),
//					left + mDescView.getWidth(),
//					mTop + mDescView.getHeight() + mHeaderView.getHeight());
			<span style="color:#ff0000;">requestLayout();</span>
		}

		@Override
		public void onViewReleased(View releasedChild, float xvel, float yvel) {
			int top = getPaddingTop();
			if (yvel > 0 || (yvel == 0 && mDragOffset > 0.5f)) {
				top += mDragRange;
			}
			mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);
			invalidate();
		}

		@Override
		public int getViewVerticalDragRange(View child) {
			return mDragRange;
		}

		@Override
		public int clampViewPositionVertical(View child, int top, int dy) {
			final int topBound = getPaddingTop();
			final int bottomBound = getHeight() - mHeaderView.getHeight()
					- mHeaderView.getPaddingBottom();

			final int newTop = Math.min(Math.max(top, topBound), bottomBound);
			return newTop;
		}

	}

	<span style="color:#ff0000;">@Override
	public void computeScroll() {
		if (mDragHelper.continueSettling(true)) {
			ViewCompat.postInvalidateOnAnimation(this);
		}
	}</span>

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		final int action = MotionEventCompat.getActionMasked(ev);

		if ((action != MotionEvent.ACTION_DOWN)) {
			mDragHelper.cancel();
			return super.onInterceptTouchEvent(ev);
		}

		if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
			mDragHelper.cancel();
			return false;
		}

		final float x = ev.getX();
		final float y = ev.getY();
		boolean interceptTap = false;

		switch (action) {
		case MotionEvent.ACTION_DOWN: {
			mInitialMotionX = x;
			mInitialMotionY = y;
			interceptTap = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
			break;
		}

		case MotionEvent.ACTION_MOVE: {
			final float adx = Math.abs(x - mInitialMotionX);
			final float ady = Math.abs(y - mInitialMotionY);
			final int slop = mDragHelper.getTouchSlop();
			/* useless */
			if (ady > slop && adx > ady) {
				mDragHelper.cancel();
				return false;
			}
		}
		}

		return mDragHelper.shouldInterceptTouchEvent(ev)|| interceptTap;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		<span style="color:#ff0000;">mDragHelper.processTouchEvent(ev);</span>

		final int action = ev.getAction();
		final float x = ev.getX();
		final float y = ev.getY();

		boolean isHeaderViewUnder = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
		switch (action & MotionEventCompat.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN: {
			mInitialMotionX = x;
			mInitialMotionY = y;
			break;
		}

		case MotionEvent.ACTION_UP: {
			final float dx = x - mInitialMotionX;
			final float dy = y - mInitialMotionY;
			final int slop = mDragHelper.getTouchSlop();
			if (dx * dx + dy * dy < slop * slop && isHeaderViewUnder) {
				if (mDragOffset == 0) {
					smoothSlideTo(1f);
				} else {
					smoothSlideTo(0f);
				}
			}
			break;
		}
		}

		return isHeaderViewUnder && isViewHit(mHeaderView, (int) x, (int) y)
				|| isViewHit(mDescView, (int) x, (int) y);
	}

	private boolean isViewHit(View view, int x, int y) {
		int[] viewLocation = new int[2];
		view.getLocationOnScreen(viewLocation);
		int[] parentLocation = new int[2];
		this.getLocationOnScreen(parentLocation);
		int screenX = parentLocation[0] + x;
		int screenY = parentLocation[1] + y;
		return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth()
				&& screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight();
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		measureChildren(widthMeasureSpec, heightMeasureSpec);

		int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
		int maxHeight = MeasureSpec.getSize(heightMeasureSpec);

		setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
				resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		mDragRange = getHeight() - mHeaderView.getHeight();

		mHeaderView.layout(0, mTop, r, mTop + mHeaderView.getMeasuredHeight());

		mDescView.layout(0, mTop + mHeaderView.getMeasuredHeight(), r, mTop + b);
	}
}<span style="color:#c00000;">
</span>




代码下载地址:https://github.com/flavienlaurent/flavienlaurent.com

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值