当前位置: 首页 > news >正文

手机免费创网站/企业网站建设的基本流程

手机免费创网站,企业网站建设的基本流程,品牌网站策划方案,知道创宇 wordpressScrollView 触摸事件一览 ScrollView 继承于 FrameLayout,属于 ViewGroup 控件。View 树的触摸事件是从 ViewGroup 的 dispatchTouchEvent 开始分发的。先判断 ViewGroup 的 onInterceptTouchEvent 是否拦截,同时这里也可以调用 ViewGroup 的 requestDis…

ScrollView 触摸事件一览

ScrollView 继承于 FrameLayout,属于 ViewGroup 控件。View 树的触摸事件是从 ViewGroup 的 dispatchTouchEvent 开始分发的。先判断 ViewGroup 的 onInterceptTouchEvent 是否拦截,同时这里也可以调用 ViewGroup 的 requestDisallowInterceptTouchEvent 让 ViewGroup 不调用 onInterceptTouchEvent,如果事件被拦截,则调用 ViewGroup 的超类即 View 的 dispatchTouchEvent,反之,则调用子视图的 dispatchTouchEvent 。

上图针对的是 ACTION_DOWN 事件。

  • Activity 接收事件后,由 dispatchTouchEvent进行分发。Activity 的 dispatchTouchEvent 如果不调用 super (无论返回 true or false)则事件不会向下分发。所以一般 activity 的 dispatchTouchEvent 需要调用 super 才能向下分发。
  • ViewGroup 的 dispatchTouchEvent,用来向下分发事件。如果此方法内不调用super,直接返回 true 则代表直接消费终止。返回 false 代表不在分发直接交给复层处理。调用 super 则会执行 onInterceptTouchEvent 方法。
  • onInterceptTouchEvent 方法用来判断当前 ViewGroup 是否需要拦截此事件。如果拦截返回 true,则直接调用当前 ViewGroup 的 onTouchEvent 自己处理。不需要拦截返回 false 或者直接调用 super 即可。
  • 最下次 View 的 dispatchTouchEvent 接受到事件后,true 代表消费终止,false 则直接调用自己的 onTouchEvent处理事件。注意 View 没有 onInterceptTouchEvent,此方法只有 ViewGroup 有。
  • down 事件在哪个View 消费了,那么 move 和 up 事件就只会从上向下传递到这个 view,不会继续向下传递。

View 之 dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event) {...boolean result = false;...ListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {result = true;}if (!result && onTouchEvent(event)) {result = true;}...return result;
}
复制代码

根据源码可知:

  • 首先执行 dispatchTouchEvent 方法。
  • 在 dispatchTouchEvent 方法中先执行 onTouch 方法,后执行 onClick 方法(onClick方法在onTouchEvent中执行)。
  • 如果 onTouch 返回false或者 mOnTouchListener 为null(控件没有设置 setOnTouchListener 方法)或者控件不是enable的情况下会调运 onTouchEvent , dispatchTouchEvent 返回值与 onTouchEvent 返回一样。
  • 如果不是enable的设置了 onTouch 方法也不会执行,只能通过重写控件的 onTouchEvent 方法处理, dispatchTouchEvent 返回值与 onTouchEvent 返回一样。
  • 如果是enable且 onTouch 返回true情况下, dispatchTouchEvent 直接返回true,不会调用 onTouchEvent 方法。

View 之 onTouchEvent

public boolean onTouchEvent(MotionEvent event) {...if ((viewFlags & ENABLED_MASK) == DISABLED) {return (((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));}...if (((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {switch (event.getAction()) {case MotionEvent.ACTION_UP:...if (mPerformClick == null) {mPerformClick = new PerformClick();}if (!post(mPerformClick)) {performClick();}...break;case MotionEvent.ACTION_DOWN:...break;case MotionEvent.ACTION_CANCEL:...break;case MotionEvent.ACTION_MOVE:...break;}return true;}return false;
}public boolean performClick() {final boolean result;final ListenerInfo li = mListenerInfo;if (li != null && li.mOnClickListener != null) {li.mOnClickListener.onClick(this);result = true;} else {result = false;}return result;
}
复制代码

根据源码可知:

  • onTouchEvent 方法中会在ACTION_UP分支中触发 onClick 的监听。
  • 当 dispatchTouchEvent 在进行事件分发的时候,只有前一个action返回true,才会触发下一个action(也就是说dispatchTouchEvent返回true才会进行下一次action派发)。

ViewGroup 之 dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {...final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!disallowIntercept) {intercepted = onInterceptTouchEvent(ev);ev.setAction(action);} else {intercepted = false;}} else {intercepted = true;}...// Dispatch to touch targets.if (mFirstTouchTarget == null) {// No touch targets so treat this as an ordinary view.handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);} else {...TouchTarget target = mFirstTouchTarget;while (target != null) {final TouchTarget next = target.next;if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {handled = true;} else {if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {handled = true;}}predecessor = target;target = next;}}if (canceled|| actionMasked == MotionEvent.ACTION_UP|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {resetTouchState();} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {final int actionIndex = ev.getActionIndex();final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);removePointersFromTouchTargets(idBitsToRemove);}
}
复制代码

根据源码可知:

  • 使用变量intercepted来标记ViewGroup是否拦截Touch事件的传递,mGroupFlags可以根据 requestDisallowInterceptTouchEvent 方法来设置是否拦截的标志 FLAG_DISALLOW_INTERCEPT 。
  • FLAG_DISALLOW_INTERCEPT一旦设置之后,ViewGroup将无法拦截除ACTION_DOWN以外的其他点击事件。ViewGroup会在ACTION_DOWN事件到来时做重置状态的操作。在resetTouchState方法中重置FLAG_DISALLOW_INTERCEPT标记位。因此,子View调用requestDisallowInterceptTouchEvent方法并不能影响ViewGroup对ACTION_DOWN事件的处理。当ViewGroup决定拦截事件后,那么后续的点击事件将默认交给它处理并且不再调用它的onInterceptTouchEvent方法。FLAG_DISALLOW_INTERCEPT标记位的作用是让ViewGroup不再拦截事件,前提是ViewGroup不拦截ACTION_DOWN事件。
  • dispatchTransformedTouchEvent 将Touch事件传递给特定的子View。在该方法中为一个递归调用,会递归调用 dispatchTouchEvent 方法。在 dispatchTouchEvent 中如果子View为ViewGroup并且Touch没有被拦截那么递归调用 dispatchTouchEvent ,如果子View为View那么就会调用其 onTouchEvent 。 dispatchTransformedTouchEvent 方法如果返回true则表示子View消费掉该事件,同时进入该if判断。
  • dispatchTransformedTouchEvent 方法返回false,即子View的 onTouchEvent 返回false(即Touch事件未被消费)。那么该子View就无法继续处理ACTION_MOVE事件和ACTION_UP事件。
  • dispatchTransformedTouchEvent 会调用递归调用 dispatchTouchEvent 和 onTouchEvent ,所以 dispatchTransformedTouchEvent 的返回值实际上是由 onTouchEvent 决定的。

ViewGroup 之 dispatchTransformedTouchEvent

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) {...if (newPointerIdBits == oldPointerIdBits) {if (child == null || child.hasIdentityMatrix()) {if (child == null) {handled = super.dispatchTouchEvent(event);} else {final float offsetX = mScrollX - child.mLeft;final float offsetY = mScrollY - child.mTop;event.offsetLocation(offsetX, offsetY);handled = child.dispatchTouchEvent(event);event.offsetLocation(-offsetX, -offsetY);}return handled;}transformedEvent = MotionEvent.obtain(event);} else {transformedEvent = event.split(newPointerIdBits);}}...
}
复制代码

根据源码可知:

  • 当child == null时会将Touch事件传递给该ViewGroup自身的dispatchTouchEvent()处理,即super.dispatchTouchEvent(event)(也就是View的这个方法,因为ViewGroup的父类是View);当child != null时会调用该子view(当然该view可能是一个View也可能是一个ViewGroup)的dispatchTouchEvent(event)处理,即child.dispatchTouchEvent(event)。
  • Android事件派发是先传递到最顶级的ViewGroup,再由ViewGroup递归传递到View的。
  • 在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。
  • 子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。

常见滑动冲突场景

外部滑动和内部滑动方向不一致

  1. ViewPager和Fragment配合使用组成的页面滑动效果。这种冲突的解决方式,一般都是根据水平滑动还是竖直滑动(滑动的距离差)来判断到底是由谁来拦截事件。
  2. 外部滑动和内部滑动方向一致。内外两层同时能上下滑动或者能同时左右滑动。这种一般都是根据业务来进行区分。
  3. 以上两种场景的嵌套

滑动冲突的解决方式

外部拦截法

外部拦截法,就是所有事件都先经过父容器的拦截处理,由父容器来决定是否拦截。这种方式需要重写父容器的onInterceptTouchEvent方法,伪代码如下:

public boolean onInterceptTouchEvent(MotionEvent ev) {boolean intercepted = false;int x = (int) ev.getX();int y = (int) ev.getY();switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:intercepted = false;break;case MotionEvent.ACTION_MOVE:if (父容器需要当前点击事件) {intercepted = true;} else {intercepted = false;}break;case MotionEvent.ACTION_UP:intercepted=false;break;default:break;}mLastXIntercept = x;mLastYIntercept = y;return intercepted;
}
复制代码
  • 不拦截ACTION_DOWN事件。一旦父容器拦截ACTION_DOWN,则后续的ACTION_MOVE和ACTION_UP事件都会直接交由父容器处理,无法传递给子元素。
  • ACTION_MOVE事件根据具体需求来决定是否拦截。
  • ACTION_UP事件必须返回false,ACTION_UP事件本身没什么意义,但如果父容器在ACTION_UP返回true会导致子元素无法接收ACTION_UP事件,无法响应onClick事件。

内部拦截法

内部拦截法是指父容器不拦截任何事件,所有事件都传递给子元素。内部拦截法需要配合requestDisallowInterceptTouchEvent方法才能正常工作。这种方式需要重写子元素的dispatchTouchEvent方法,伪代码如下:

public boolean dispatchTouchEvent(MotionEvent ev) {int x = (int) ev.getX();int y = (int) ev.getY();switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:getParent().requestDisallowInterceptTouchEvent(true);break;case MotionEvent.ACTION_MOVE:int deltaX = x - mLastX;int deltaY = y - mLastY;if (父容器需要当前点击事件) {getParent().requestDisallowInterceptTouchEvent(false);}break;case MotionEvent.ACTION_UP:break;default:break;}mLastX = x;mLastY = y;return super.dispatchTouchEvent(ev);
}
复制代码

父元素需要默认拦截除ACTION_DOWN事件以外的其他事件,父元素修改如下:

public boolean onInterceptTouchEvent(MotionEvent ev) {if (ev.getAction()==MotionEvent.ACTION_DOWN) {return false;} else {return true;}
}
复制代码

ACTION_DOWN事件并不受FLAG_DISALLOW_INTERCEPT这个标记位的控制。一旦父容器拦截ACTION_DOWN事件,那么所有的事件都无法传递到子元素中去。

ScrollView 触摸事件流程

onInterceptTouchEvent

onInterceptTouchEvent 所进行的处理,即在 ACTION_DOWN 资源初始化,ACTION_MOVE 判断是否开始拖动手势,ACTION_CANCEL && ACTION_UP 中进行资源释放。这里涉及了多指触摸的处理。

预处理

if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {return true;
}
复制代码

用 mIsBeingDragged 变量来保存当前是否已经开始进行拖动手势,这个后面会讲到,同时当前分发事件类型为 ACTION_MOVE,那么直接返回 true,即拦截事件向子视图进行分发。

ACTION_MOVE

if (!inChild((int) ev.getX(), (int) y)) {mIsBeingDragged = false;recycleVelocityTracker();break;
}
复制代码

如果触摸事件没有作用于子视图范围内,那么就不处理,同时释放速度跟踪器(一般用于 fling 手势的判定)。

mLastMotionY = y;
mActivePointerId = ev.getPointerId(0);
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);mScroller.computeScrollOffset();
mIsBeingDragged = !mScroller.isFinished();startNestedScroll(SCROLL_AXIS_VERTICAL);
复制代码

mLastMotionY 记录按下时的坐标信息,mActivePointerId 记录当前分发触摸事件的手指 id,这个一般用于多指的处理,initOrResetVelocityTracker 初始化速度跟踪器,同时使用 addMovement 记录当前触摸事件信息,mScroller 是一般用于 fling 手势处理,这里的作用是处理上一次的 fling,startNestedScroll 则是嵌套滚动机制的知识了。

ACTION_MOVE

final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {break;
}
final int pointerIndex = ev.findPointerIndex(activePointerId);
if (pointerIndex == -1) {break;
}
final int y = (int) ev.getY(pointerIndex);
final int yDiff = Math.abs(y - mLastMotionY);
if (yDiff > mTouchSlop && (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {mIsBeingDragged = true;mLastMotionY = y;initVelocityTrackerIfNotExists();mVelocityTracker.addMovement(ev);final ViewParent parent = getParent();if (parent != null) {parent.requestDisallowInterceptTouchEvent(true);}
}
复制代码

对 mActivePointerId 进行是否为有效的判断,如果有效,则通过 findPointerIndex 获取作用手指 id 的下标,记录为 pointerIndex ,为什么要获取这个值,我们知道现在的手机屏幕都是支持多指触摸的,所以我们需要根据某个按下的手指的触摸信息来进行处理。yDiff 是滑动的距离,mTouchSlop 则是 SDK 定义的可作为判定是否开始进行拖动的距离常量,可以通过 ViewConfiguration 的 getScaledTouchSlop 获取,如果大于这个值,我们可以认为开始了拖动的手势。 getNestedScrollAxes 这个同样是用于嵌套滚动机制的。如果开始了拖动手势,mIsBeingDragged 标记为 true,同样使用速度跟踪器记录信息,这里还会调用 ViewParent 的 requestDisallowInterceptTouchEvent,防止父视图拦截了事件,即 onInterceptTouchEvent。

ACTION_CANCEL && ACTION_UP

mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
recycleVelocityTracker();
stopNestedScroll();
复制代码

进行一些释放资源的操作,比如 mIsBeingDragged 设置为 false,释放速度跟踪器等等。

ACTION_UP 是所有的手指(多指触摸)抬起时分发的事件,而 ACTION_CANCEL 则是触摸取消事件类型,一般什么时候会分发这个事件呢?举个例子,如果某个子视图已经消费了 ACTION_DOWN,即在这个事件分发时,向父视图传递了 true 的返回值,那么一般情况下,父视图不会再拦截接下来的事件,比如 ACTION_MOVE 等,但是如果父视图在这种情况下,还拦截了事件传递,即在 onInterceptTouch 中返回了 true,那么在 ViewGroup 的 dispatchTouchEvent 中会给已经确认消费事件的子视图分发一个 TOUCH_CANCEL 的事件。
复制代码

ACTION_POINTER_UP

多指触摸时,某个手指抬起时分发的事件。

final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {final int newPointerIndex = pointerIndex == 0 ? 1 : 0;mLastMotionY = (int) ev.getY(newPointerIndex);mActivePointerId = ev.getPointerId(newPointerIndex);if (mVelocityTracker != null) {mVelocityTracker.clear();}
}
复制代码

当某个手指抬起时,而这个手指刚好是我们当前使用的,则重新初始化资源。

onTouchEvent

onTouchEvent 和 onInterceptTouchEvent 处理有些相似,主要是在 TOUCH_MOVE 中在判定为拖动手势后进行真正的业务逻辑处理,同时在 ACTION_UP 中根据速度跟踪器的获取的速度,判定是否符合 fling 手势,如果符合,则使用 Scroller 进行计算。

ACTION_DOWN

if (getChildCount() == 0) {return false;
}
if ((mIsBeingDragged = !mScroller.isFinished())) {final ViewParent parent = getParent();if (parent != null) {parent.requestDisallowInterceptTouchEvent(true);}
}if (!mScroller.isFinished()) {mScroller.abortAnimation();
}mLastMotionY = (int) ev.getY();
mActivePointerId = ev.getPointerId(0);
startNestedScroll(SCROLL_AXIS_VERTICAL);
复制代码

onTouchEvent 在 ACTION_DOWN 事件分发中,主要是进行资源初始化,同时也处理上一次的 fling 任务,比如调用 Scroller 的 abortAnimation,如果 Scroller 还没结束 fling 计算,则中止处理。

ACTION_MOVE

final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
if (activePointerIndex == -1) {break;
}
final int y = (int) ev.getY(activePointerIndex);
int deltaY = mLastMotionY - y;
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {// 嵌套滚动处理deltaY -= mScrollConsumed[1];vtev.offsetLocation(0, mScrollOffset[1]);mNestedYOffset += mScrollOffset[1];
}
if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {final ViewParent parent = getParent();if (parent != null) {parent.requestDisallowInterceptTouchEvent(true);}mIsBeingDragged = true;if (deltaY > 0) {deltaY -= mTouchSlop;} else {deltaY += mTouchSlop;}
}if (mIsBeingDragged) {/// 业务逻辑
}
复制代码

进行多指处理,获取指定手指的触摸事件信息。mIsBeingDragged 为 false,同时会再进行一次拖动手势的判定,判定逻辑和 onInterceptTouchEvent 中类似,如果 mIsBeingDragged 为 true,则开始进行真正的逻辑处理。

if (mIsBeingDragged) {mLastMotionY = y - mScrollOffset[1];final int oldY = mScrollY;final int range = getScrollRange();final int overscrollMode = getOverScrollMode();boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true)&& !hasNestedScrollingParent()) {mVelocityTracker.clear();}final int scrolledDeltaY = mScrollY - oldY;final int unconsumedY = deltaY - scrolledDeltaY;if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {mLastMotionY -= mScrollOffset[1];vtev.offsetLocation(0, mScrollOffset[1]);mNestedYOffset += mScrollOffset[1];} else if (canOverscroll) {final int pulledToY = oldY + deltaY;if (pulledToY < 0) {mEdgeGlowTop.onPull((float) deltaY / getHeight(),ev.getX(activePointerIndex) / getWidth());if (!mEdgeGlowBottom.isFinished()) {mEdgeGlowBottom.onRelease();}} else if (pulledToY > range) {mEdgeGlowBottom.onPull((float) deltaY / getHeight(),1.f - ev.getX(activePointerIndex) / getWidth());if (!mEdgeGlowTop.isFinished()) {mEdgeGlowTop.onRelease();}}if (mEdgeGlowTop != null&& (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {postInvalidateOnAnimation();}}
}
复制代码

EdgeEffect 是用于拖动时,边缘的阴影效果。

ACTION_UP

if (mIsBeingDragged) {final VelocityTracker velocityTracker = mVelocityTracker;velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);if ((Math.abs(initialVelocity) > mMinimumVelocity)) {flingWithNestedDispatch(-initialVelocity);}mActivePointerId = INVALID_POINTER;endDrag();
}
复制代码

当手指全部抬起时,可以使用速度跟踪器进行 fling 手势的判定,同时释放资源。通过 getYVelocity 获取速度,在判断是否可以作为 fling 手势处理,mMaximumVelocity 是处理的最大速度,mMinimumVelocity 是处理的最小速度,这两个值同样可以通过 ViewConfiguration 的 getScaledMaximumFlingVelocity 和 getScaledMinimumFlingVelocity 获取。一般情况对 fling 的处理是通过 Scroller 进行处理的,因为这里涉及复杂的数学知识,而 Scroller 可以帮我们简化这里的操作,使用如下:

int height = getHeight() - mPaddingBottom - mPaddingTop;
int bottom = getChildAt(0).getHeight();mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0,Math.max(0, bottom - height), 0, height/2);postInvalidateOnAnimation();
复制代码

通过传递当前拖动手势速度值来调用 fling 进行处理,然后在 computeScrollOffset 方法中,进行真正的滚动处理:

public void computeScroll() {if (mScroller.computeScrollOffset()) {int oldX = mScrollX;int oldY = mScrollY;int x = mScroller.getCurrX();int y = mScroller.getCurrY();if (oldX != x || oldY != y) {final int range = getScrollRange();final int overscrollMode = getOverScrollMode();final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,0, mOverflingDistance, false);onScrollChanged(mScrollX, mScrollY, oldX, oldY);if (canOverscroll) {if (y < 0 && oldY >= 0) {mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());} else if (y > range && oldY <= range) {mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());}}}postInvalidateOnAnimation();}
}
复制代码

Scroller 并不会为我们进行滚动处理,它只是提供了计算的模型,通过调用 computeScrollOffset 进行计算,如果返回 true,表示计算还没结束,然后通过 getCurrX 或 getCurrY 获取计算后的值,最后进行真正的滚动处理,比如调用 scrollTo 等等,这里需要注意的是,需要调用 invalidate 来确保进行下一次的 computeScroll 调用,这里使用的 postInvalidateOnAnimation 其作用是类似的。

ACTION_CANCEL

if (mIsBeingDragged && getChildCount() > 0) {if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {postInvalidateOnAnimation();}mActivePointerId = INVALID_POINTER;endDrag();
}
复制代码

释放资源。

ACTION_POINTER_DOWN

当有新的手指按下时分发的事件。

final int index = ev.getActionIndex();
mLastMotionY = (int) ev.getY(index);
mActivePointerId = ev.getPointerId(index);
复制代码

以新按下的手指的信息重新计算。

ACTION_POINTER_UP

处理和 onInterceptTouch 一致。

SDK 工具类

系统已经提供 GestureDetector 来进行手势的判定,我们只需要在相应的手势回调方法中进行我们的业务逻辑即可。还有更强大的 ViewDragHelper。

http://www.lbrq.cn/news/1270405.html

相关文章:

  • 网站建设云梦/泰安seo推广
  • 绵阳专门做网站的公司有哪些/杭州关键词优化平台
  • 关于asp网站模板下载/有没有免费的写文案的软件
  • 安阳网站制作/网络营销最新案例
  • 外贸建站是什么意思/广州seo公司排行
  • wordpress建立视频网站/东莞网站建设方案外包
  • 郑州网站推广¥做下拉去118cr/3000行业关键词
  • 怎么让人搜索到自己做的网站/成都网站建设方案推广
  • 上海做产地证在哪个网站录入/网站建站开发
  • 用腾讯云做网站的好处/大连网站seo
  • 网站开发包含网页设计吗/百度学术官网入口
  • 做关于星空的网站/某企业网站的分析优化与推广
  • 信誉好的网站建设公司/济南seo顾问
  • 做行政关注什么类型的网站/网上教育培训机构哪家好
  • 宿迁建设企业网站/做直销去哪里找客户
  • 如何做网站内链/优化seo排名
  • 网站头像设计免费制作/深圳网络推广的公司
  • 满满正能量网站/专门开发小程序的公司
  • 个人网站备案 备注/大丰seo排名
  • 无锡定制网站制作公司/百度搜索指数在线查询
  • 保密和档案网站建设方案/中国舆情网
  • 网页设计与网站建设步骤/百度一下京东
  • 网站建设功能描述书/站长工具seo优化系统
  • 网站ftp根目录/今天重大新闻国内最新消息
  • 外贸社交网站排名/晋江怎么交换友情链接
  • 桂林网站seo/网络广告营销策划方案
  • wordpress 4.8教程/杭州seo排名
  • 大连模板建站软件/河南最新消息
  • 邢台网络公司做网站/简单的个人网页制作html
  • 做网站 单页数量/百度账号申诉
  • ubuntu22.04离线一键安装gpu版docker
  • 【redis】基于工业界技术分享的内容总结
  • 李宏毅深度学习教程 第4-5章 CNN卷积神经网络+RNN循环神经网络
  • Android ConstraintLayout 使用详解
  • React核心:组件化与虚拟DOM揭秘
  • 《嵌入式C语言笔记(十六):字符串搜索、动态内存与函数指针精要》