原文地址:http://blog.csdn.net/yebo0505/article/details/42779441
首先感谢这两篇文章提供的思路和代码
http://blog.csdn.net/chenupt/article/details/41478303
http://blog.csdn.net/singwhatiwanna/article/details/42614953
实现原理:
点击屏幕,遍历所有的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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
/**
* @Title: DragTipsLayout.java
* @Package com.eeb.dragredview
* @Description: TODO(模仿QQ拖动消息提示)
* @author luquan yebo0505@foxmail.com
* @date 2015-1-4 上午11:57:48
* @version V1.0
*/
package
com
.
eeb
.
dragtipslayout
;
import
android
.
content
.
Context
;
import
android
.
graphics
.
Canvas
;
import
android
.
graphics
.
Color
;
import
android
.
graphics
.
Paint
;
import
android
.
graphics
.
Path
;
import
android
.
graphics
.
Rect
;
import
android
.
graphics
.
RectF
;
import
android
.
util
.
AttributeSet
;
import
android
.
view
.
MotionEvent
;
import
android
.
view
.
View
;
import
android
.
view
.
ViewGroup
;
import
android
.
widget
.
LinearLayout
;
public
class
DragTipsLayout
extends
LinearLayout
{
// 默认定点圆半径
public
static
final
float
DEFAULT_RADIUS
=
10
;
private
int
[
]
mLocationInScreen
=
new
int
[
2
]
;
private
Paint
paint
;
private
Path
path
;
private
boolean
dragging
;
// 手势滑动是的动态坐标
float
touchX
=
0
;
float
touchY
=
0
;
// 锚点坐标,两个圆圆心(手势滑动坐标和要拖动的bageview的中心)的中间点
float
anchorX
=
0
;
float
anchorY
=
0
;
// 定点圆半径
float
radius
=
DEFAULT_RADIUS
;
// 要拖动的View的中心坐标
private
float
mCenterX
;
private
float
mCenterY
;
private
View
targetView
;
public
DragTipsLayout
(
Context
context
)
{
super
(
context
)
;
init
(
)
;
}
public
DragTipsLayout
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
,
attrs
)
;
init
(
)
;
}
@
Override
protected
void
onLayout
(
boolean
changed
,
int
l
,
int
t
,
int
r
,
int
b
)
{
super
.
onLayout
(
changed
,
l
,
t
,
r
,
b
)
;
this
.
getLocationOnScreen
(
mLocationInScreen
)
;
//DragTisLayout 在屏幕上的位置
}
@
Override
protected
void
dispatchDraw
(
Canvas
canvas
)
{
// TODO Auto-generated method stub
super
.
dispatchDraw
(
canvas
)
;
if
(
dragging
)
{
// 画拖动的效果,这里计算的是在拖动的过程中,半径变化,但两个圆始终保持半径一样,如果半径一大一小不同,需要另外计算
float
distance
=
(
float
)
Math
.
sqrt
(
Math
.
pow
(
touchY
-
mCenterY
,
2
)
+
Math
.
pow
(
touchX
-
mCenterX
,
2
)
)
;
radius
=
-
distance
/
20
+
DEFAULT_RADIUS
;
paint
.
setColor
(
Color
.
RED
)
;
if
(
radius
>
5
)
{
float
offsetX
=
(
float
)
(
radius *
Math
.
sin
(
Math
.
atan
(
(
touchY
-
mCenterY
)
/
(
touchX
-
mCenterX
)
)
)
)
;
float
offsetY
=
(
float
)
(
radius *
Math
.
cos
(
Math
.
atan
(
(
touchY
-
mCenterY
)
/
(
touchX
-
mCenterX
)
)
)
)
;
float
x1
=
mCenterX
-
offsetX
;
float
y1
=
mCenterY
+
offsetY
;
float
x2
=
touchX
-
offsetX
;
float
y2
=
touchY
+
offsetY
;
float
x3
=
touchX
+
offsetX
;
float
y3
=
touchY
-
offsetY
;
float
x4
=
mCenterX
+
offsetX
;
float
y4
=
mCenterY
-
offsetY
;
path
.
reset
(
)
;
path
.
moveTo
(
x1
,
y1
)
;
path
.
quadTo
(
anchorX
,
anchorY
,
x2
,
y2
)
;
path
.
lineTo
(
x3
,
y3
)
;
path
.
quadTo
(
anchorX
,
anchorY
,
x4
,
y4
)
;
path
.
lineTo
(
x1
,
y1
)
;
canvas
.
drawCircle
(
mCenterX
,
mCenterY
,
radius
,
paint
)
;
canvas
.
drawPath
(
path
,
paint
)
;
}
if
(
targetView
!=
null
)
{
//计算字体所占的位置大小,获取拖动的badge大小,以确定在拖动过程中,字体居中,画出拖动的view跟badgeview一样
String
num
=
(
(
BadgeView
)
targetView
)
.
getText
(
)
.
toString
(
)
;
Rect
rect
=
new
Rect
(
)
;
paint
.
getTextBounds
(
num
,
0
,
num
.
length
(
)
,
rect
)
;
RectF
rectF
=
new
RectF
(
touchX
-
targetView
.
getWidth
(
)
/
2
,
touchY
-
targetView
.
getHeight
(
)
/
2
,
touchX
+
targetView
.
getWidth
(
)
/
2
,
touchY
+
targetView
.
getHeight
(
)
/
2
)
;
canvas
.
drawOval
(
rectF
,
paint
)
;
paint
.
setColor
(
Color
.
WHITE
)
;
canvas
.
drawText
(
num
,
touchX
-
rect
.
centerX
(
)
,
touchY
-
rect
.
centerY
(
)
,
paint
)
;
}
}
}
private
void
init
(
)
{
setWillNotDraw
(
false
)
;
path
=
new
Path
(
)
;
paint
=
new
Paint
(
)
;
paint
.
setAntiAlias
(
true
)
;
paint
.
setStyle
(
Paint
.
Style
.
FILL_AND_STROKE
)
;
paint
.
setStrokeWidth
(
1
)
;
paint
.
setColor
(
Color
.
RED
)
;
}
@
Override
public
boolean
dispatchTouchEvent
(
MotionEvent
event
)
{
switch
(
event
.
getAction
(
)
)
{
case
MotionEvent
.
ACTION_DOWN
:
findTouchTarget
(
this
,
event
.
getRawX
(
)
,
event
.
getRawY
(
)
)
;
touchX
=
event
.
getX
(
)
;
touchY
=
event
.
getY
(
)
;
anchorX
=
touchX
;
anchorY
=
touchY
;
break
;
case
MotionEvent
.
ACTION_MOVE
:
touchX
=
event
.
getX
(
)
;
touchY
=
event
.
getY
(
)
;
anchorX
=
(
mCenterX
+
touchX
)
/
2
;
anchorY
=
(
mCenterY
+
touchY
)
/
2
;
break
;
case
MotionEvent
.
ACTION_UP
:
if
(
radius
>
5
&&
targetView
!=
null
)
targetView
.
setVisibility
(
View
.
VISIBLE
)
;
dragging
=
false
;
break
;
}
invalidate
(
)
;
//如果没有拖动, 事件分发不做处理,正常使用点击,长按事件,如果return true,可以理解为事件不传递到子类,各个子view的点击,滑动无效
if
(
dragging
)
return
true
;
else
return
super
.
dispatchTouchEvent
(
event
)
;
}
//遍历DragTipsLayout下所有的view
private
void
findTouchTarget
(
ViewGroup
viewGrop
,
float
x
,
float
y
)
{
for
(
int
i
=
0
;
i
<
viewGrop
.
getChildCount
(
)
;
i
++
)
{
View
temp
=
viewGrop
.
getChildAt
(
i
)
;
if
(
temp
instanceof
ViewGroup
)
{
ViewGroup
tempViewGrop
=
(
ViewGroup
)
temp
;
findTouchTarget
(
tempViewGrop
,
x
,
y
)
;
//递归查找
}
else
if
(
temp
instanceof
BadgeView
&&
isTouchBadgeView
(
temp
,
x
,
y
)
)
{
//判断是否是BadgeView并且点击屏幕的坐标在BadgeView的区域范围内
dragging
=
true
;
}
}
}
private
boolean
isTouchBadgeView
(
View
view
,
float
x
,
float
y
)
{
int
[
]
location
=
new
int
[
2
]
;
view
.
getLocationOnScreen
(
location
)
;
int
left
=
location
[
0
]
;
int
top
=
location
[
1
]
;
int
right
=
left
+
view
.
getMeasuredWidth
(
)
;
int
bottom
=
top
+
view
.
getMeasuredHeight
(
)
;
if
(
view
.
getVisibility
(
)
==
View
.
VISIBLE
&&
y
>=
top
&&
y
<=
bottom
&&
x
>=
left
&&
x
<=
right
)
{
//依据遍历找到的BadgeView,计算对应在DragTipsLayout上的坐标点
mCenterX
=
(
left
+
right
)
/
2
-
-
mLocationInScreen
[
0
]
;
mCenterY
=
(
top
+
bottom
)
/
2
-
mLocationInScreen
[
1
]
;
targetView
=
view
;
view
.
setVisibility
(
View
.
INVISIBLE
)
;
return
true
;
}
return
false
;
}
}
|