当前位置:网站首页>Android修炼系列(十一),kotlin静态方法

Android修炼系列(十一),kotlin静态方法

2022-01-15 02:25:44 mb61c1dbbb44788

android:layout_height=“70dp”
android:text=“可拖拽”
android:gravity=“center”
android:textColor=“#fff”
android:background=“#6495ED”
/>
</com.blog.a.drag.DragViewGroup>

是不是非常省事,博客的栗子我都上传到了 gitHub上,感兴趣的可以下载看下。

源码

本篇文章主要分析下,当触摸事件开始到结束,processTouchEvent的处理过程:

public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
}

MotionEvent.ACTION_DOWN

当手指刚接触屏幕时,会触发ACTION_DOWN 事件,通过MotionEvent我们能获取到点击事件发生的 x, y 坐标,注意这里的getX/getY的坐标是相对于当前view而言的。Pointer是触摸点的概念,一个MotionEvent可能会包含多个Pointer触摸点的信息,而每个Pointer触摸点都会有一个自己的id和index。具体往下看。

case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
final int pointerId = ev.getPointerId(0);
final View toCapture = findTopChildUnder((int) x, (int) y);

saveInitialMotion(x, y, pointerId);

tryCaptureViewForDrag(toCapture, pointerId);
// mTrackingEdges默认是0,可通过ViewDragHelper#setEdgeTrackingEnabled(int)
// 来设置,用来控制触碰边缘回调onEdgeTouched
final int edgesTouched = mInitialEdgesTouched[pointerId];
if ((edgesTouched & mTrackingEdges) != 0) {
mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
}
break;
}

这里的findTopChildUnder方法是用来获取当前x, y坐标点所在的view,默认是最上层的,当然我们也可以通过callback#getOrderedChildIndex(int) 接口来自定义view遍历顺序,代码见下:

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;
}

这里的saveInitialMotion方法是用来保存当前触摸位置信息,其中getEdgesTouched方法用来判断x, y是否位于此viewGroup边缘之外,并返回保存相应result结果。todo:下篇准备写一下关于位运算符的文章,很有意思。

private void saveInitialMotion(float x, float y, int pointerId) {
ensureMotionHistorySizeForId(pointerId);
mInitialMotionX[pointerId] = mLastMotionX[pointerId] = x;
mInitialMotionY[pointerId] = mLastMotionY[pointerId] = y;
mInitialEdgesTouched[pointerId] = getEdgesTouched((int) x, (int) y);
mPointersDown |= 1 << pointerId;
}

其中tryCaptureViewForDrag方法内,mCapturedView是当前触摸的视图view,如果相同则直接返回,否则会进行mCallback#tryCaptureView(View, int)判断,这个是不是很眼熟,我们可以重写这个回调来控制toCapture这个view能否被捕获,即能否被拖拽操作。

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;
}

这里的captureChildView方法用来保存信息,并设置拖拽状态。能注意到,这里还有个捕获view是否是child view的判断。

public void captureChildView(@NonNull View childView, int activePointerId) {
if (childView.getParent() != mParentView) {
throw new IllegalArgumentException("captureChildView: parameter must be a descendant "

  • “of the ViewDragHelper’s tracked parent view (” + mParentView + “)”);
    }

mCapturedView = childView;
mActivePointerId = activePointerId;
mCallback.onViewCaptured(childView, activePointerId);
setDragState(STATE_DRAGGING);
}

MotionEvent.ACTION_POINTER_DOWN

当用户又使用一个手指接触屏幕时,会触发ACTION_POINTER_DOWN 事件,与上面的ACTION_DOWN 相似,就不细展开了。由于ViewDragHelper一次只能操作一个视图,所以这里会先进行状态判断,如果视图还未被捕获拖动,则逻辑与上面的ACTION_POINTER_DOWN一致,反之,会判断触摸点是否在当前视图内,如果符合条件,则更新Pointer,这里很重要,体现在ui效果上就是,一个手指按住view,另一个手指仍然可以拖拽此view。

case MotionEvent.ACTION_POINTER_DOWN: {
final int pointerId = ev.getPointerId(actionIndex);
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);

saveInitialMotion(x, y, pointerId);
// A ViewDragHelper can only manipulate one view at a time.
if (mDragState == STATE_IDLE) {
// If we’re idle we can do anything! Treat it like a normal down event.
final View toCapture = findTopChildUnder((int) x, (int) y);
tryCaptureViewForDrag(toCapture, pointerId);

final int edgesTouched = mInitialEdgesTouched[pointerId];
if ((edgesTouched & mTrackingEdges) != 0) {
mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
}
} else if (isCapturedViewUnder((int) x, (int) y)) {
tryCaptureViewForDrag(mCapturedView, pointerId);
}
break;
}

MotionEvent.ACTION_MOVE

当手指在屏幕移动时,如果视图正在被拖动,则会先判断当前mActivePointerId是否有效,无效则跳过当前move事件。随后获取当前x, y并计算与上次x, y移动距离。之后触发dragTo拖动逻辑,最后保存保存这次的位置。核心方法dragTo分析见下文:

case MotionEvent.ACTION_MOVE: {
if (mDragState == STATE_DRAGGING) {
// If pointer is invalid then skip the ACTION_MOVE.
if (!isValidPointerForActionMove(mActivePointerId)) break;

final int index = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(index);
final float y = ev.getY(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.

}
break;
}

在move过程中,通过dragTo方法来传入目标x, y 和横向和竖向的偏移量,并通过callback回调来通知开发者,开发者可重写clampViewPositionHorizontal与clampViewPositionVertical这两个回调方法,来自定义clampedX,clampedY目标位置。随后使用offsetLeftAndRight和offsetTopAndBottom 方法分别在相应的方向偏移(clampedX - oldLeft)和(clampedY - oldTo)的像素。最后触发onViewPositionChanged位置修改的回调。

private void dragTo(int left, int top, int dx, int dy) {
int clampedX = left;
int clampedY = top;
final int oldLeft = mCapturedView.getLeft();
final int oldTop = mCapturedView.getTop();
if (dx != 0) {
clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx);
ViewCompat.offsetLeftAndRight(mCapturedView, clampedX - oldLeft);
}
if (dy != 0) {
clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy);
ViewCompat.offsetTopAndBottom(mCapturedView, clampedY - oldTop);
}

if (dx != 0 || dy != 0) {
final int clampedDx = clampedX - oldLeft;
final int clampedDy = clampedY - oldTop;
mCallback.onViewPositionChanged(mCapturedView, clampedX, clampedY,
clampedDx, clampedDy);
}
}

如果当手指在屏幕移动时,发现视图未处于拖动状态呢?首先会去检查是否有其他Pointer是否有效。随后触发边缘拖动回调,随后再进行状态检查,应该是为了避免此时状态由未拖动->拖动状态了,如:smoothSlideViewTo方法就有这个能力。如果此时mDragState处于未拖动状态,则会重新获取x,y 所在视图view并重新设置拖拽状态,这个逻辑与down逻辑一样。

case MotionEvent.ACTION_MOVE: {
if (mDragState == STATE_DRAGGING) {
// If pointer is invalid then skip the ACTION_MOVE.

} else {
// Check to see if any pointer is now over a draggable view.
final int pointerCount = ev.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
final int pointerId = ev.getPointerId(i);

// If pointer is invalid then skip the ACTION_MOVE.
if (!isValidPointerForActionMove(pointerId)) continue;

final float x = ev.getX(i);
final float y = ev.getY(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);

写在最后

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从哪里入手去学习,对此我整理了一些资料,需要的可以免费分享给大家

我的 【Github】会分享一些关于Android进阶方面的知识,也会分享一下最新的面试题~

如果你熟练掌握GitHub中列出的知识点,相信将会大大增加你通过前两轮技术面试的几率!这些内容都供大家参考,互相学习。

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包——————可以在我的 【Github】阅读下载,最后觉得有帮助、有需要的朋友可以点个赞

Android修炼系列(十一),kotlin静态方法_Android

Android修炼系列(十一),kotlin静态方法_移动开发_02

Android修炼系列(十一),kotlin静态方法_移动开发_03

版权声明
本文为[mb61c1dbbb44788]所创,转载请带上原文链接,感谢
https://blog.51cto.com/u_15466190/4928949

随机推荐