在Android開發中,我們常常用到ListView和GridView,而有的時候系統的ListView,GridView並不能滿足我們的需求,所以我們需要自己定義一個ListView或者GridView,我的上一篇文章中就是自定義的一個左右滑動刪除item的例子,大家有興趣的可以去看看 Android 使用Scroller實現絢麗的ListView左右滑動刪除Item效果,今天這篇文章就給大家來自定義GridView的控件,GridView主要是來顯示網格的控件,在Android的開發中使用很普通,相對於TextView,Button這些控件來說要來的複雜些,今天給大家帶來長按GridView的item,然後將其拖拽其他item上面,使得GridView的item發生交換,比較典型的就是我們的Launcher,網上有很多關於GridView的拖動的Demo,但是大部分都是相同的,而且存在一些Bug,而且大部分都是點擊GridView的item然後進行拖動,或者item之間不進行實時交換,今天給大家更加詳細的介紹GridView拖拽,並且將Demo做的更完美,大家更容易接受,也許很多人聽到這個感覺實現起來很複雜,就關掉的這篇文章,其實告訴大家,只要知道了思路就感覺一點都不復雜了,不信大家可以接着往下看看,首先還是跟大家說說實現的思路
看完上面的這些思路你是不是找到了些感覺了呢,心裏癢癢的想動手試試吧,好吧,接下來就帶大家根據思路來實現可拖拽的GridView,新建一個項目就叫DragGridView
新建一個類DragGridView繼承GridView,先來看看DragGridView的代碼,然後在根據代碼進行相關的講解
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
|
package
com.example.draggridview;
import
android.app.Activity;
import
android.content.Context;
import
android.graphics.Bitmap;
import
android.graphics.PixelFormat;
import
android.graphics.Rect;
import
android.os.Handler;
import
android.os.Vibrator;
import
android.util.AttributeSet;
import
android.view.Gravity;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.WindowManager;
import
android.widget.AdapterView;
import
android.widget.GridView;
import
android.widget.ImageView;
/**
* @blog http://blog.csdn.net/xiaanming
*
* @author xiaanming
*
*/
public
class
DragGridView
extends
GridView{
/**
* DragGridView的item長按響應的時間, 默認是1000毫秒,也可以自行設置
*/
private
long
dragResponseMS =
1000
;
/**
* 是否可以拖拽,默認不可以
*/
private
boolean
isDrag =
false
;
private
int
mDownX;
private
int
mDownY;
private
int
moveX;
private
int
moveY;
/**
* 正在拖拽的position
*/
private
int
mDragPosition;
/**
* 剛開始拖拽的item對應的View
*/
private
View mStartDragItemView =
null
;
/**
* 用於拖拽的鏡像,這裏直接用一個ImageView
*/
private
ImageView mDragImageView;
/**
* 震動器
*/
private
Vibrator mVibrator;
private
WindowManager mWindowManager;
/**
* item鏡像的佈局參數
*/
private
WindowManager.LayoutParams mWindowLayoutParams;
/**
* 我們拖拽的item對應的Bitmap
*/
private
Bitmap mDragBitmap;
/**
* 按下的點到所在item的上邊緣的距離
*/
private
int
mPoint2ItemTop ;
/**
* 按下的點到所在item的左邊緣的距離
*/
private
int
mPoint2ItemLeft;
/**
* DragGridView距離屏幕頂部的偏移量
*/
private
int
mOffset2Top;
/**
* DragGridView距離屏幕左邊的偏移量
*/
private
int
mOffset2Left;
/**
* 狀態欄的高度
*/
private
int
mStatusHeight;
/**
* DragGridView自動向下滾動的邊界值
*/
private
int
mDownScrollBorder;
/**
* DragGridView自動向上滾動的邊界值
*/
private
int
mUpScrollBorder;
/**
* DragGridView自動滾動的速度
*/
private
static
final
int
speed =
20
;
/**
* item發生變化回調的接口
*/
private
OnChanageListener onChanageListener;
public
DragGridView(Context context) {
this
(context,
null
);
}
public
DragGridView(Context context, AttributeSet attrs) {
this
(context, attrs,
0
);
}
public
DragGridView(Context context, AttributeSet attrs,
int
defStyle) {
super
(context, attrs, defStyle);
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mStatusHeight = getStatusHeight(context);
//獲取狀態欄的高度
}
private
Handler mHandler =
new
Handler();
//用來處理是否爲長按的Runnable
private
Runnable mLongClickRunnable =
new
Runnable() {
@Override
public
void
run() {
isDrag =
true
;
//設置可以拖拽
mVibrator.vibrate(
50
);
//震動一下
mStartDragItemView.setVisibility(View.INVISIBLE);
//隱藏該item
//根據我們按下的點顯示item鏡像
createDragImage(mDragBitmap, mDownX, mDownY);
}
};
/**
* 設置回調接口
* @param onChanageListener
*/
public
void
setOnChangeListener(OnChanageListener onChanageListener){
this
.onChanageListener = onChanageListener;
}
/**
* 設置響應拖拽的毫秒數,默認是1000毫秒
* @param dragResponseMS
*/
public
void
setDragResponseMS(
long
dragResponseMS) {
this
.dragResponseMS = dragResponseMS;
}
@Override
public
boolean
dispatchTouchEvent(MotionEvent ev) {
switch
(ev.getAction()){
case
MotionEvent.ACTION_DOWN:
mDownX = (
int
) ev.getX();
mDownY = (
int
) ev.getY();
//根據按下的X,Y座標獲取所點擊item的position
mDragPosition = pointToPosition(mDownX, mDownY);
if
(mDragPosition == AdapterView.INVALID_POSITION){
return
super
.dispatchTouchEvent(ev);
}
//使用Handler延遲dragResponseMS執行mLongClickRunnable
mHandler.postDelayed(mLongClickRunnable, dragResponseMS);
//根據position獲取該item所對應的View
mStartDragItemView = getChildAt(mDragPosition - getFirstVisiblePosition());
//下面這幾個距離大家可以參考我的博客上面的圖來理解下
mPoint2ItemTop = mDownY - mStartDragItemView.getTop();
mPoint2ItemLeft = mDownX - mStartDragItemView.getLeft();
mOffset2Top = (
int
) (ev.getRawY() - mDownY);
mOffset2Left = (
int
) (ev.getRawX() - mDownX);
//獲取DragGridView自動向上滾動的偏移量,小於這個值,DragGridView向下滾動
mDownScrollBorder = getHeight() /
4
;
//獲取DragGridView自動向下滾動的偏移量,大於這個值,DragGridView向上滾動
mUpScrollBorder = getHeight() *
3
/
4
;
//開啓mDragItemView繪圖緩存
mStartDragItemView.setDrawingCacheEnabled(
true
);
//獲取mDragItemView在緩存中的Bitmap對象
mDragBitmap = Bitmap.createBitmap(mStartDragItemView.getDrawingCache());
//這一步很關鍵,釋放繪圖緩存,避免出現重複的鏡像
mStartDragItemView.destroyDrawingCache();
break
;
case
MotionEvent.ACTION_MOVE:
int
moveX = (
int
)ev.getX();
int
moveY = (
int
) ev.getY();
//如果我們在按下的item上面移動,只要不超過item的邊界我們就不移除mRunnable
if
(!isTouchInItem(mStartDragItemView, moveX, moveY)){
mHandler.removeCallbacks(mLongClickRunnable);
}
break
;
case
MotionEvent.ACTION_UP:
mHandler.removeCallbacks(mLongClickRunnable);
mHandler.removeCallbacks(mScrollRunnable);
break
;
}
return
super
.dispatchTouchEvent(ev);
}
/**
* 是否點擊在GridView的item上面
* @param itemView
* @param x
* @param y
* @return
*/
private
boolean
isTouchInItem(View dragView,
int
x,
int
y){
if
(dragView ==
null
){
return
false
;
}
int
leftOffset = dragView.getLeft();
int
topOffset = dragView.getTop();
if
(x < leftOffset || x > leftOffset + dragView.getWidth()){
return
false
;
}
if
(y < topOffset || y > topOffset + dragView.getHeight()){
return
false
;
}
return
true
;
}
@Override
public
boolean
onTouchEvent(MotionEvent ev) {
if
(isDrag && mDragImageView !=
null
){
switch
(ev.getAction()){
case
MotionEvent.ACTION_MOVE:
moveX = (
int
) ev.getX();
moveY = (
int
) ev.getY();
//拖動item
onDragItem(moveX, moveY);
break
;
case
MotionEvent.ACTION_UP:
onStopDrag();
isDrag =
false
;
break
;
}
return
true
;
}
return
super
.onTouchEvent(ev);
}
/**
* 創建拖動的鏡像
* @param bitmap
* @param downX
* 按下的點相對父控件的X座標
* @param downY
* 按下的點相對父控件的X座標
*/
private
void
createDragImage(Bitmap bitmap,
int
downX ,
int
downY){
mWindowLayoutParams =
new
WindowManager.LayoutParams();
mWindowLayoutParams.format = PixelFormat.TRANSLUCENT;
//圖片之外的其他地方透明
mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left;
mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top - mStatusHeight;
mWindowLayoutParams.alpha =
0
.55f;
//透明度
mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE ;
mDragImageView =
new
ImageView(getContext());
mDragImageView.setImageBitmap(bitmap);
mWindowManager.addView(mDragImageView, mWindowLayoutParams);
}
/**
* 從界面上面移動拖動鏡像
*/
private
void
removeDragImage(){
if
(mDragImageView !=
null
){
mWindowManager.removeView(mDragImageView);
mDragImageView =
null
;
}
}
/**
* 拖動item,在裏面實現了item鏡像的位置更新,item的相互交換以及GridView的自行滾動
* @param x
* @param y
*/
private
void
onDragItem(
int
moveX,
int
moveY){
mWindowLayoutParams.x = moveX - mPoint2ItemLeft + mOffset2Left;
mWindowLayoutParams.y = moveY - mPoint2ItemTop + mOffset2Top - mStatusHeight;
mWindowManager.updateViewLayout(mDragImageView, mWindowLayoutParams);
//更新鏡像的位置
onSwapItem(moveX, moveY);
//GridView自動滾動
mHandler.post(mScrollRunnable);
}
/**
* 當moveY的值大於向上滾動的邊界值,觸發GridView自動向上滾動
* 當moveY的值小於向下滾動的邊界值,觸犯GridView自動向下滾動
* 否則不進行滾動
*/
private
Runnable mScrollRunnable =
new
Runnable() {
@Override
public
void
run() {
int
scrollY;
if
(moveY > mUpScrollBorder){
scrollY = speed;
mHandler.postDelayed(mScrollRunnable,
25
);
}
|