当前位置:网站首页>Android修煉系列(十一),kotlin靜態方法

Android修煉系列(十一),kotlin靜態方法

2022-01-15 02:28:02 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://chowdera.com/2022/01/202201150224018059.html

随机推荐