简世博客

一个简单的世界——博客空间,写了一些Android相关的技术文章,和一些点滴的想法

0%

笔记 -- android悬浮窗的拖动实现踩坑

想要实现一个可拖动的悬浮窗,原理上非常简单:根据action_move时的event坐标偏移,去修改view的位置即可。
但是实际实现上踩了个小坑。

详细踩坑:

平时获取event的坐标,都是用event.getX() 和 event.getY() ,但是实操下来发现,这样会导致拖动效果不跟手, 拖动的位置比手势移动的位置要小,并且会有明显的位置抖动。

在这个拖动的场景下,其实应该使用 event.getRawX() 和 event.getRawY() 来获取坐标。

event.getX() 和 event.getRawX() 的区别:

1
2
3
4
5
6
7
8
9
/**
* {@link #getX(int)} for the first pointer index (may be an
* arbitrary pointer identifier).
*
* @see #AXIS_X
*/
public final float getX() {
return nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* Returns the original raw X coordinate of this event. For touch
* events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views.
*
* @see #getX(int)
* @see #AXIS_X
*/
public final float getRawX() {
return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}

getRawX()使用的是相对屏幕的坐标,而 getX()使用的是相对view的坐标,在view本身随手势移动的时候,这个相对view的坐标就会发生错误的偏移,导致拖动效果不跟手。

实际代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
binding.root.setOnTouchListener(object : View.OnTouchListener {
var xDown = 0f
var yDown = 0f

var interactorTranslationXWhenDown = 0f
var interactorTranslationYWhenDown = 0f

override fun onTouch(v: View?, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
xDown = event.rawX
yDown = event.rawY
interactorTranslationXWhenDown = interactorTranslationX
interactorTranslationYWhenDown = interactorTranslationY
}
MotionEvent.ACTION_MOVE -> {
val dx = event.rawX - xDown
val dy = event.rawY - yDown
interactorTranslationX =
interactorTranslationXWhenDown + dx
interactorTranslationY =
interactorTranslationYWhenDown + dy
Timber.e("dx:${dx} dy:${dy}")
updateTranslation()
}
else -> {}
}
return false
}

})

注:如果想写的简单一点,也可以直接使用

1
2
interactorTranslationX = event.rawX
interactorTranslationY = event.rawY

这样也能实现基本的拖动效果,只是view会跳到手指右下角的位置来拖动,有点难用。