上一篇我们用到了ViewDragHelper的几个回调 tryCaptureView clampViewPositionHorizontal、 clampViewPositionVertical、 clampViewPositionHorizontal、 onEdgeDragStarted,在源码中我们又分别分析到了一些回调的时机,今天继续分析下还没有用过的回调,方便更全面的认识。
首先看下getOrderedChildIndex(),这个在哪个地方用到了呢
                                            /**                                  * Find the topmost child under the given point within the parent view's coordinate system.                                  * The child order is determined using {@link Callback#getOrderedChildIndex(int)}.                                  *                                  * @param x X position to test in the parent's coordinate system                                  * @param y Y position to test in the parent's coordinate system                                  * @return The topmost child view under (x, y) or null if none found.                                  */                                  public View findTopChildUnder(int x, int y) {                                  final int childCount = mParentView.getChildCount();                                  for (int i = childCount - 1; i >= 0; i--) {                                  final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i));                                  if (x >= child.getLeft() && x < child.getRight() &&                                  y >= child.getTop() && y < child.getBottom()) {                                  return child;                                  }                                  }                                  return null;                                  }                      
我们在上篇文章中,看到这个方法,但是我只是告诉大家这个方法是在找最顶层的子View,从for循环中我们看到,是从最上层View开始找的,但是呢,你也可以改变这个查找的顺序 ,怎么改呢 :mCallback.getorderedChildIndex(i)
                                            public int getOrderedChildIndex(int index) {                                  return index;                                  }                      
这个方法默认返回的就是index,因为如果不复写的话,就默认顺序找,好,既然这个我们现在再来改改 首先我们让
                                           VDHLayout extends FrameLayout                      
这个布局就是重叠的,这个效果图就不用贴了,最上面的就是那个叫Drag edge的text view , 不过之前我们只对它在边界触发时候进行了调动,自然,我们在点击拖动的时候 ,它自然不会动。 那么现在, 几个TextView重叠了,如果我们点击来拖动的话是一个都不会动,why? 这个还请大家自己思考 。 那么我们就必然要打破这个默认的寻找子View的顺序
                                            public int getOrderedChildIndex(int index) {                                  // TODO Auto-generated method stub                                  int cCount = getChildCount();                                  if(index == cCount - 1 ){                                  index = cCount - 2;                                  }                                  return index;                                  }                      
ok,这样一来,当系统找到最顶层的view的时候 ,我们改变了顺以,让它的index值减去1,这样下面的一个子View就成为了这个事件的接收者,然后就没有然后了,随意玩~

再看看onEdgeLock(),这个有点意思,这里是个误区 ,它并不像字面的意思那样,返回true就是不能从哪个边缘滑动,那它到底到底个啥意思 ,首先看我自己修改的
                                            public int clampViewPositionVertical(View child, int top, int dy) {                                  return top;                                  }                      
默认返回原始的top,这样可以让它到处滚
                                            /**                                  * 锁定哪个边界                                  */                                  @Override                                  public boolean onEdgeLock(int edgeFlags) {                                  // TODO Auto-generated method stub                                   if(edgeFlags == ViewDragHelper.EDGE_LEFT){                                  return true;                                  }else{                                  return false;                                  }                                  }                      
从表面上看是不能从左边滑动,对不?其实不是这样 ,如果你运行下,你会看到好像没有什么叼用,我自己研究的时候 ,也是把我愣住 了,也以为是这样,我们行从源码中看看这个到底是哪里用到了
                                            private void reportNewEdgeDrags(float dx, float dy, int pointerId) {                                  int dragsStarted = 0;                                  if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_LEFT)) {                                  dragsStarted |= EDGE_LEFT;                                  }                                  if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_TOP)) {                                  dragsStarted |= EDGE_TOP;                                  }                                  if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_RIGHT)) {                                  dragsStarted |= EDGE_RIGHT;                                  }                                  if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_BOTTOM)) {                                  dragsStarted |= EDGE_BOTTOM;                                  }                                                                   if (dragsStarted != 0) {                                  mEdgeDragsInProgress[pointerId] |= dragsStarted;                                  mCallback.onEdgeDragStarted(dragsStarted, pointerId);                                  }                                  }                                                                   private boolean checkNewEdgeDrag(float delta, float odelta, int pointerId, int edge) {                                  final float absDelta = Math.abs(delta);                                  final float absODelta = Math.abs(odelta);                                                                   if ((mInitialEdgesTouched[pointerId] & edge) != edge || (mTrackingEdges & edge) == 0 ||                                  (mEdgeDragsLocked[pointerId] & edge) == edge ||                                  (mEdgeDragsInProgress[pointerId] & edge) == edge ||                                  (absDelta <= mTouchSlop && absODelta <= mTouchSlop)) {                                  return false;                                  }                                  if (absDelta < absODelta * 0.5f && mCallback.onEdgeLock(edge)) {                                  mEdgeDragsLocked[pointerId] |= edge;                                  return false;                                  }                                  return (mEdgeDragsInProgress[pointerId] & edge) == 0 && absDelta > mTouchSlop;                                  }                      
ok,我直接给大家看个重点,在checkNewEdgeDrag的第二个if语句中,假如这个时候是EDGE_LEFT触发了,也就是从左边开始向右滑动了,我们再看看这个判断语句 ,它说,如果x方向滑动的距离 小于 y方向滑动距离的二分之一 && 回调onEdgeLock返回true,才不让滑动,否则允许滑动。 不知道大家是否看明白 了, 通俗说,如果我从左到右滑动,却出现了,y方向滑动的距离的一半比x方向滑动的距离还要大,这完全是扯谈的,所以你可以通过onEdgeLock返回true来禁止这样扯蛋的事情 ,如果你的onEdgeLock返回false(默认的就是返回false),那么这种扯蛋的事情能出现~ 现在大家知道该怎么去滑动了吧,这个只能靠大家自己领悟了~

再看看onViewReleased(),为什么看这个方法,因为接下来,我要让大家明白如何对子View进行位移的,这里涉及到了scroller的理解与应用 ,在上篇中有提到,如果不熟悉,需要先看看
                                            protected void onLayout(boolean changed, int left, int top, int right,                                  int bottom) {                                  // TODO Auto-generated method stub                                  super.onLayout(changed, left, top, right, bottom);                                  mOrigX = (int) mReleaseView.getX();                                  mOrigY = (int) mReleaseView.getY();                                  }                      
这里我们定位mRelease的初始位置
                                            /**                                  * 手指抬起,释放capturedChild的时候                                  */                                  @Override                                  public void onViewReleased(View releasedChild, float xvel, float yvel) {                                  if(releasedChild == mReleaseView){                                  mDragHelper.settleCapturedViewAt(mOrigX, mOrigY);                                  invalidate();                                  }                                  }                      

我们调动了invalidate()来重绘,那么必然就会调用ViewGroup的computScroll()方法
                                            public void computeScroll() {                                  if(mDragHelper.continueSettling(true)){                                  invalidate();                                  }                                  }                      
在这个方法中,我们再用mDragHelper.continueSetting(true)来不断计算位置,并重新调用invalidate()来重绘,直到不计算为止,大家有没有感觉到这就是scroller的用法 ,现在大家可以试试,是不是可以可以做到反弹的效果~
好了,我们看看这是怎么实现的
                                            public boolean settleCapturedViewAt(int finalLeft, int finalTop) {                                  if (!mReleaseInProgress) {                                  throw new IllegalStateException("Cannot settleCapturedViewAt outside of a call to " +                                  "Callback#onViewReleased");                                  }                                                                   return forceSettleCapturedViewAt(finalLeft, finalTop,                                  (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),                                  (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId));                                  }                      
                                                     private boolean forceSettleCapturedViewAt(int finalLeft, int finalTop, int xvel, int yvel) {                                        final int startLeft = mCapturedView.getLeft();                                        final int startTop = mCapturedView.getTop();                                        final int dx = finalLeft - startLeft;                                        final int dy = finalTop - startTop;                                                                               if (dx == 0 && dy == 0) {                                        // Nothing to do. Send callbacks, be done.                                        mScroller.abortAnimation();                                        setDragState(STATE_IDLE);                                        return false;                                        }                                                                               final int duration = computeSettleDuration(mCapturedView, dx, dy, xvel, yvel);                                        mScroller.startScroll(startLeft, startTop, dx, dy, duration);                                                                               setDragState(STATE_SETTLING);                                        return true;                                        }                           
                                                              public boolean continueSettling(boolean deferCallbacks) {                                              if (mDragState == STATE_SETTLING) {                                              boolean keepGoing = mScroller.computeScrollOffset();                                              final int x = mScroller.getCurrX();                                              final int y = mScroller.getCurrY();                                              final int dx = x - mCapturedView.getLeft();                                              final int dy = y - mCapturedView.getTop();                                                                                           if (dx != 0) {                                              mCapturedView.offsetLeftAndRight(dx);                                              }                                              if (dy != 0) {                                              mCapturedView.offsetTopAndBottom(dy);                                              }                                                                                           if (dx != 0 || dy != 0) {                                              mCallback.onViewPositionChanged(mCapturedView, x, y, dx, dy);                                              }                                                                                           if (keepGoing && x == mScroller.getFinalX() && y == mScroller.getFinalY()) {                                              // Close enough. The interpolator/scroller might think we're still moving                                              // but the user sure doesn't.                                              mScroller.abortAnimation();                                              keepGoing = false;                                              }                                                                                           if (!keepGoing) {                                              if (deferCallbacks) {                                              mParentView.post(mSetIdleRunnable);                                              } else {                                              setDragState(STATE_IDLE);                                              }                                              }                                              }                                                                                           return mDragState == STATE_SETTLING;                                              }                                
就是scroller的用法 ,现在值得一提的事是,在SettleCapturedViewAt的方法中,获取的x和y方向上的速度,这里传入了一个mActiviePointerId,这个到底什么用呢,这个返回的就是当前手指离开屏幕的速度 ,也就是说,手指离开屏幕的那一刻,滑动的越快,速度越大,那么在forceSettleCapturedViewAt中的duration时间就越短,这样动画的速度就越快,如果我不想这样呢
                                           mDragHelper.smoothSlideViewTo(releasedChild, mOrigX, mOrigY)                      
这样就平滑的滚回去了~ 至于 continueSettling(true) 为什么是传入true,这里就是一个handler处理message机制 ,不过这里我暂时没明白用处在哪,不过 传入true肯定不会错~

最后我们看看最后两个方法getViewHorizontalDragRange()和getViewVerticalDragRange() 修改如下
                                           VDHLayout extends LinearLayout                      
                                                    <com.example.viewdraghelper_01.VDHLayout xmlns:android="http://schemas.android.com/apk/res/android"                                        android:layout_width="match_parent"                                        android:layout_height="match_parent"                                        android:orientation="vertical" >                                                                               <Button                                        android:id="@+id/tv_01"                                        android:layout_width="100dp"                                        android:layout_height="100dp"                                        android:layout_gravity="center_horizontal"                                        android:layout_margin="10dp"                                        android:background="#44ff0000"                                        android:gravity="center"                                        android:text="Drag me" />                                                                               <TextView                                        android:id="@+id/tv_02"                                        android:layout_width="100dp"                                        android:layout_height="100dp"                                        android:layout_gravity="center_horizontal"                                        android:layout_margin="10dp"                                        android:background="#44ff0000"                                        android:gravity="center"                                        android:text="Drag and release me" />                                                                               <TextView                                        android:id="@+id/tv_03"                                        android:layout_width="100dp"                                        android:layout_height="100dp"                                        android:layout_gravity="center_horizontal"                                        android:layout_margin="10dp"                                        android:background="#44ff0000"                                        android:gravity="center"                                        android:text="Drag edge" />                                                                              </com.example.viewdraghelper_01.VDHLayout>                           
                                            @Override                                  public int getViewHorizontalDragRange(View child) {                                  return super.getViewHorizontalDragRange(child);                                  }                                                                   @Override                                  public int getViewVerticalDragRange(View child) {                                  return super.getViewVerticalDragRange(child);                                  }                      

我们改成了button后,就不能移动移动了,通过在第一篇中的参考博客,我们可以知道View事件的传递机制,这个button可以被点击,也就是说clickable=true , 它就会消费这个事件,那么父viewGroup就不能处理这个后续的move 、up事件了,那么我们要怎么做呢
                                            @Override                                  public int getViewHorizontalDragRange(View child) {                                  return mDragHelper.getEdgeSize();                                  }                                                                   @Override                                  public int getViewVerticalDragRange(View child) {                                  return mDragHelper.getEdgeSize();                                  }                      
其实这里只要返回大于0的数就可以(我在源码中一个判断条件 ,就是是否大于0),我们返回的触发边缘的宽度。
那么是不是觉得不合情理啊,没关系,我们来看看 在onInterceptTouchEvent中,我们用mDragHelper.shouldInterceptTouchEvent来决定是否拦截,既然这个down事件无法拦截,就拦截MOVE事件,看 shouldInterceptTouchEvent中的MOVE处理
                                            case MotionEvent.ACTION_MOVE: {                                  // First to cross a touch slop over a draggable view wins. Also report edge drags.                                  final int pointerCount = MotionEventCompat.getPointerCount(ev);                                  for (int i = 0; i < pointerCount; i++) {                                  final int pointerId = MotionEventCompat.getPointerId(ev, i);                                  final float x = MotionEventCompat.getX(ev, i);                                  final float y = MotionEventCompat.getY(ev, i);                                  final float dx = x - mInitialMotionX[pointerId];                                  final float dy = y - mInitialMotionY[pointerId];                                                                   final View toCapture = findTopChildUnder((int) x, (int) y);                                  final boolean pastSlop = toCapture != null && checkTouchSlop(toCapture, dx, dy);                                  if (pastSlop) {                                  // check the callback's                                  // getView[Horizontal|Vertical]DragRange methods to know                                  // if you can move at all along an axis, then see if it                                  // would clamp to the same value. If you can't move at                                  // all in every dimension with a nonzero range, bail.                                  final int oldLeft = toCapture.getLeft();                                  final int targetLeft = oldLeft + (int) dx;                                  final int newLeft = mCallback.clampViewPositionHorizontal(toCapture,                                  targetLeft, (int) dx);                                  final int oldTop = toCapture.getTop();                                  final int targetTop = oldTop + (int) dy;                                  final int newTop = mCallback.clampViewPositionVertical(toCapture, targetTop,                                  (int) dy);                                  final int horizontalDragRange = mCallback.getViewHorizontalDragRange(                                  toCapture);                                  final int verticalDragRange = mCallback.getViewVerticalDragRange(toCapture);                                  if ((horizontalDragRange == 0 || horizontalDragRange > 0                                  && newLeft == oldLeft) && (verticalDragRange == 0                                  || verticalDragRange > 0 && newTop == oldTop)) {                                  break;                                  }                                  }                                  reportNewEdgeDrags(dx, dy, pointerId);                                  if (mDragState == STATE_DRAGGING) {                                  // Callback might have started an edge drag                                  break;                                  }                                                                   if (pastSlop && tryCaptureViewForDrag(toCapture, pointerId)) {                                  break;                                  }                                  }                                  saveLastMotion(ev);                                  break;                      
从上篇文章中,我们看到postSlop什么时候为true,那就是 getViewHorizontalDragRange()和getViewVerticalDragRange()的返回值是>0的时候 ,现在我们正是这样做了,所以可以看最后一个if语句中的tryCaptruedViewForDrag
                                            boolean tryCaptureViewForDrag(View toCapture, int pointerId) {                                  if (toCapture == mCapturedView && mActivePointerId == pointerId) {                                  // Already done!                                  return true;                                  }                                  if (toCapture != null && mCallback.tryCaptureView(toCapture, pointerId)) {                                  mActivePointerId = pointerId;                                  captureChildView(toCapture, pointerId);                                  return true;                                  }                                  return false;                                  }                      
只要我们在回调tryCaptureView中,让相应的view返回true,这样ACTION_MOVE事件就能返回true,这样就截断了事件,所以处理ACTION_MOVE事件,就是VDHLayout的onTouchEvent处理,接下来就看processTouchEvent中的ACTION_MOVE
                                            case MotionEvent.ACTION_MOVE: {                                  if (mDragState == STATE_DRAGGING) {                                  final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);                                  final float x = MotionEventCompat.getX(ev, index);                                  final float y = MotionEventCompat.getY(ev, index);                                  final int idx = (int) (x - mLastMotionX[mActivePointerId]);                                  final int idy = (int) (y - mLastMotionY[mActivePointerId]);                                                                   dragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop() + idy, idx, idy);                                                                   saveLastMotion(ev);                                  } else {                                  // Check to see if any pointer is now over a draggable view.                                  final int pointerCount = MotionEventCompat.getPointerCount(ev);                                  for (int i = 0; i < pointerCount; i++) {                                  final int pointerId = MotionEventCompat.getPointerId(ev, i);                                  final float x = MotionEventCompat.getX(ev, i);                                  final float y = MotionEventCompat.getY(ev, i);                                  final float dx = x - mInitialMotionX[pointerId];                                  final float dy = y - mInitialMotionY[pointerId];                                                                   reportNewEdgeDrags(dx, dy, pointerId);                                  if (mDragState == STATE_DRAGGING) {                                  // Callback might have started an edge drag.                                  break;                                  }                                                                   final View toCapture = findTopChildUnder((int) x, (int) y);                                  if (checkTouchSlop(toCapture, dx, dy) &&                                  tryCaptureViewForDrag(toCapture, pointerId)) {                                  break;                                  }                                  }                                  saveLastMotion(ev);                                  }                                  break;                                  }                      
这里在上篇已经讲过了,不多说了。

到这里呢,所有的ViewDragHelper的知识已经讲完,可能有点地方不详细,自己多动动手,参照这些,相信你很快就能明白。 如果大家在看的同时有疑问,或者发现错误的地方,欢迎提出~


更多相关文章

  1. Android手势研究(textview及listview对比验证)(二)
  2. Android(安卓)Retrofit 源码系列(三)~ 整合 RxJava、Coroutine 分
  3. android:transcriptMode用法
  4. Android(安卓)studio中给Button添加点击事件的4种方法
  5. Android下执行Runtime.getRuntime().exec后返回状态
  6. 2014.01.21(2)——— android开发实例之viewpager无限循环+自动滚
  7. 解决scrollview中内容改变后自动滑动到底部的问题
  8. android ListView的滑动效率问题
  9. Android(安卓)SD卡操作 (Environment 状态)

随机推荐

  1. MSSQLSERVER跨服务器连接(远程登录)的示
  2. t-sql/mssql用命令行导入数据脚本的SQL语
  3. mssql函数DATENAME使用示例讲解(取得当前
  4. SQLSERVER加密解密函数(非对称密钥 证书
  5. SQLSERVER全文目录全文索引的使用方法和
  6. sql实现split函数的脚本
  7. SQLSERVER启动不起来(错误9003)的解决方
  8. 2分法分页存储过程脚本实例
  9. MsSQL数据导入到Mongo的默认编码问题(正
  10. 教你轻松学会SQL Server记录轮班的技巧