概述
我們先看下源代碼中針對該工具類的注釋:
ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.
所以:ViewDragHelper 是 Android 提供的一個輔助類绒北,用于實(shí)現(xiàn)拖拽冀泻、滑動等手勢操作。它主要用于處理用戶觸摸操作,例如實(shí)現(xiàn)拖拽布局、滑動刪除等交互效果。
ViewDragHelper 能夠大大簡化開發(fā)者處理拖拽手勢的工作,使得實(shí)現(xiàn)復(fù)雜的交互效果變得更加簡單和高效梨树。不過,需要注意的是岖寞,ViewDragHelper 在一些復(fù)雜場景下可能會有一定的學(xué)習(xí)曲線抡四,并且需要注意處理好各種邊界情況,以確保用戶體驗(yàn)的流暢性仗谆。
主要功能:
ViewDragHelper 提供了以下主要功能:
1指巡、拖拽控制:可以輕松地將 ViewDragHelper 與 ViewGroup 結(jié)合使用,實(shí)現(xiàn)拖拽指定的 View胸私,這些 View 可以是任何可繪制的對象厌处,例如圖片、文本等岁疼。
2、邊界檢測:可以設(shè)置邊界缆娃,限制用戶拖拽的范圍捷绒,防止對象移出屏幕范圍或者不可見。
3贯要、速度計(jì)算:ViewDragHelper 能夠計(jì)算拖拽過程中的速度暖侨,以便根據(jù)用戶的操作進(jìn)行調(diào)整。
4崇渗、輔助動畫:支持拖拽時的動畫效果字逗,例如拖拽到一定位置釋放時自動回到指定位置京郑。
5、多手勢支持:支持多點(diǎn)觸控葫掉,可以同時拖拽多個對象些举。
使用步驟
1、初始化:在自定義的 ViewGroup 中創(chuàng)建 ViewDragHelper 實(shí)例俭厚,并重寫相應(yīng)的回調(diào)方法户魏。
2、處理觸摸事件:在 ViewGroup 的 onTouchEvent() 方法中將觸摸事件傳遞給 ViewDragHelper 處理挪挤。
3叼丑、實(shí)現(xiàn)回調(diào)方法:重寫 ViewDragHelper.Callback 中的相關(guān)方法,用于處理拖拽扛门、釋放等事件鸠信。
4、設(shè)置拖拽對象:在 ViewGroup 中設(shè)置需要拖拽的 View 對象论寨。
舉個栗子??
比如我們要實(shí)現(xiàn)一個簡單布局的拖拽症副,最常用的拖拽方式是直接在onTouch事件中處理拖拽實(shí)現(xiàn),然后通過layout函數(shù)來更新View在父布局中的相對位置
以前我們會怎么做
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
public class DraggableRelativeLayout extends RelativeLayout implements View.OnTouchListener {
private int lastX;
private int lastY;
public DraggableRelativeLayout(Context context) {
super(context);
init();
}
public DraggableRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DraggableRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
switch (action) {
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - lastX;
int deltaY = y - lastY;
// 獲取布局的當(dāng)前位置
int left = v.getLeft();
int top = v.getTop();
int right = v.getRight();
int bottom = v.getBottom();
// 根據(jù)手指移動的距離更新布局的位置
v.layout(left + deltaX, top + deltaY, right + deltaX, bottom + deltaY);
lastX = x;
lastY = y;
break;
}
return true;
}
}
如果使用ViewDragHelper會怎么寫
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import androidx.customview.widget.ViewDragHelper;
public class DraggableRelativeLayout extends RelativeLayout {
private ViewDragHelper viewDragHelper;
public DraggableRelativeLayout(Context context) {
super(context);
init();
}
public DraggableRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DraggableRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
viewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return viewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
viewDragHelper.processTouchEvent(event);
return true;
}
}
所以ViewDragHelper幫我們做了什么呢政基?
ViewDragHelper 封裝了常見的拖動操作贞铣,可以簡化代碼,并提供了更好的性能和穩(wěn)定性沮明。
比如在以上的代碼對比中辕坝。
方案一中:我們需要處理Down、Move荐健、Up事件記錄坐標(biāo)進(jìn)行計(jì)算酱畅,并更新布局
方案二中:ViewDragHelper 工具類,幫助我們封裝了拖動手勢的處理邏輯江场,只需簡單地設(shè)置拖動的邊界和拖動對象纺酸,ViewDragHelper 就會幫助你完成剩余的工作。
還有哪些我們常用的成員函數(shù)址否?
create(ViewGroup forParent, float sensitivity, Callback cb)
創(chuàng)建一個 ViewDragHelper 實(shí)例餐蔬。
參數(shù) forParent 是指定要與 ViewDragHelper 關(guān)聯(lián)的父布局。
參數(shù) sensitivity 是靈敏度佑附,表示拖動的靈敏程度樊诺,一般取 1.0f。
參數(shù) cb 是 ViewDragHelper.Callback 對象音同,用于定義拖動行為词爬。
shouldInterceptTouchEvent(MotionEvent ev)
判斷是否攔截觸摸事件。
在父布局的 onInterceptTouchEvent() 方法中調(diào)用权均,用于決定是否攔截觸摸事件交給 ViewDragHelper 處理顿膨。
processTouchEvent(MotionEvent event):
處理觸摸事件锅锨。
在父布局的 onTouchEvent() 方法中調(diào)用,用于將觸摸事件傳遞給 ViewDragHelper 處理恋沃。
captureChildView(View child, int activePointerId)
捕獲指定的子視圖必搞。
當(dāng)需要開始拖動某個視圖時調(diào)用此方法。
cancel()
取消當(dāng)前正在進(jìn)行的拖動操作芽唇。
當(dāng)需要取消拖動操作時調(diào)用此方法顾画。
flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop)
在當(dāng)前位置釋放被捕獲的視圖并執(zhí)行慣性動畫。
參數(shù) minLeft匆笤、minTop研侣、maxLeft、maxTop 為釋放后視圖的位置范圍炮捧。
settleCapturedViewAt(int finalLeft, int finalTop)
將被捕獲的視圖移動到指定位置庶诡。
參數(shù) finalLeft、finalTop 為目標(biāo)位置咆课。
smoothSlideViewTo(View child, int finalLeft, int finalTop)
平滑地將指定的子視圖移動到指定位置末誓。
參數(shù) child 是要移動的子視圖,finalLeft书蚪、finalTop 為目標(biāo)位置喇澡。
getCapturedView()
獲取當(dāng)前被捕獲的子視圖。
getViewDragState()
獲取當(dāng)前的拖動狀態(tài)殊校,返回值為 STATE_IDLE晴玖、STATE_DRAGGING 或 STATE_SETTLING。
所以我們的處理重點(diǎn)是什么呢为流?
ViewDragHelper的Create函數(shù)中呕屎,ViewDragHelper.Callback對象是一個回調(diào)接口,用于處理拖拽手勢的各個階段敬察,我們有很多針對手勢的“干預(yù)”秀睛、“探測”工作都是在這里做的。
比如我們上面的例子莲祸,只是在clampViewPositionHorizontal回調(diào)函數(shù)中限制了View在水平方面的移動范圍蹂安,就可以不讓View移出我們限制的移動區(qū)域,所以當(dāng)我們需要縮小或者擴(kuò)大水平方向的可移動范圍時虫给,我們只需要修改return返回的值即可藤抡。
再比如,如果我們想要讓View只在Y軸移動(僅豎直移動)抹估。
我們可以通過實(shí)現(xiàn)
ViewDragHelper.Callback接口,并且復(fù)寫clampViewPositionHorizontal弄兜、clampViewPositionVertical函數(shù)即可
完成代碼如下:
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import androidx.customview.widget.ViewDragHelper;
public class LimitedDraggableRelativeLayout extends RelativeLayout {
private ViewDragHelper viewDragHelper;
public LimitedDraggableRelativeLayout(Context context) {
super(context);
init();
}
public LimitedDraggableRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LimitedDraggableRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
viewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
// 限制視圖只能在水平方向上移動到指定的范圍內(nèi)
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
// 限制視圖只能在垂直方向上移動到指定的范圍內(nèi)
return Math.max(0, Math.min(getHeight() - child.getHeight(), top));
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return viewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
viewDragHelper.processTouchEvent(event);
return true;
}
}