原文地址: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 [email protected]
* @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;
}
}
|