前言
许多 Android 开发者经常会问我,要学会哪些东西才能成为一个优秀的 Android 工程师?对于这个问题,他们的描述或多或少都有些差异,但是,总体来说,我们都需要学习一系列的技能,才能成为一个优秀的 Android 工程师。
在我看来,存在这样的困惑是正常的。Android 是一个巨大并且动态的生态系统,你可能需要花好几周时间去了解并学习它相关的一些工具和概念,但是最后你会发现,它们有好多都不是很重要,或者说并不是非常有用。因此,在本文中,我将分享我在 Android 开发中所使用到的重要技能,希望能够帮到你,让你把你的精力集中到重要的事情上。
所以,今天,我将献上一份《Android知识图谱》,以自身的经验 & 所见所闻,旨在告诉大家,学习Android,实际上需要学习什么内容,希望你们会喜欢。
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);
if (checkTouchSlop(toCapture, dx, dy)
&& tryCaptureViewForDrag(toCapture, pointerId)) {
break;
}
}
saveLastMotion(ev);
}
break;
}
MotionEvent.ACTION_POINTER_UP
当处于多触摸点时,当一手指从屏幕上松开时,首先判断正在拖动视图的触摸点是否是当前触摸点,如果是,则再去检查视图上是否还有其他有效的触摸点,如果没有则释放,此时view就惯性停住了。如果还有,则清理当前up掉的触摸点数据。
case MotionEvent.ACTION_POINTER_UP: {
final int pointerId = ev.getPointerId(actionIndex);
// 判断当前触摸点是否是正在拖动视图的触摸点
if (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) {
// 检查是否有其他有效触摸点
int newActivePointer = INVALID_POINTER;
final int pointerCount = ev.getPointerCount();
// 遍历ev内触摸点
for (int i = 0; i < pointerCount; i++) {
final int id = ev.getPointerId(i);
if (id == mActivePointerId) {
// This one’s going away, skip.
continue;
}
final float x = ev.getX(i);
final float y = ev.getY(i);
// 如果在视图上,并且可拖动,则标记找到了
if (findTopChildUnder((int) x, (int) y) == mCapturedView
&& tryCaptureViewForDrag(mCapturedView, id)) {
newActivePointer = mActivePointerId;
break;
}
}
if (newActivePointer == INVALID_POINTER) {
// 如果没有发现其他触摸点在拖拽视图view,则释放掉就可以了
releaseViewForPointerUp();
}
}
// 清理当前up掉的触摸点数据
clearMotionHistory(pointerId);
break;
}
MotionEvent.ACTION_UP
当手指从屏幕上离开时,会先判断当前状态,如果此时mDragState处于拖动状态,则释放,view惯性停住。通过cancel方法改变状态,清空当前触摸点数据并接触速度检测mVelocityTracker。
case MotionEvent.ACTION_UP: {
if (mDragState == STATE_DRAGGING) {
releaseViewForPointerUp();
}
cancel();
break;
}
好了,本文到这里,关于ViewDrafHHelper的介绍就结束了。如果本文对你有用,来点个赞吧,大家的肯定也是阿呆i坚持写作的动力。
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》开源
Android优秀开源项目:
- ali1024.coding.net/public/P7/Android/git
最后
总而言之,成功是留给准备好的人的。无论是参加什么面试,都要做好充足的准备,注意好面试的礼仪和穿着,向面试官表现出自己的热忱与真诚就好。即使最后没有过关,也要做好经验的总结,为下一次面试做好充足准备。
这里我为大家准备了一些我在面试后整理的面试专题资料,除了面试题,还总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料分享给大家,希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
毕竟不管遇到什么样的面试官,去面试首先最主要的就是自己的实力,只要实力够硬,技术够强,就不怕面试拿不到offer!
为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!
.(img-ShEmvSDx-1649661159969)]
[外链图片转存中…(img-v6NngKbt-1649661159971)]
为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!
[外链图片转存中…(img-1rhOPQP4-1649661159971)]