Android中ListView组件的Item拖动(Item改变顺序)

http://blog.csdn.net/mayingcai1987/archive/2011/04/25/6362325.aspx

  1. /*
  2. * Copyright (C) 2008 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the “License”);
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. *      http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an “AS IS” BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.flora;
  17. import android.content.Context;
  18. import android.content.SharedPreferences;
  19. import android.content.res.Resources;
  20. import android.graphics.Bitmap;
  21. import android.graphics.PixelFormat;
  22. import android.graphics.Rect;
  23. import android.graphics.drawable.Drawable;
  24. import android.util.AttributeSet;
  25. import android.view.GestureDetector;
  26. import android.view.Gravity;
  27. import android.view.MotionEvent;
  28. import android.view.View;
  29. import android.view.ViewConfiguration;
  30. import android.view.ViewGroup;
  31. import android.view.WindowManager;
  32. import android.view.GestureDetector.SimpleOnGestureListener;
  33. import android.widget.AdapterView;
  34. import android.widget.ImageView;
  35. import android.widget.ListView;
  36. public class DragAndDropListView extends ListView {
  37. private ImageView mDragView;
  38. private WindowManager mWindowManager;
  39. private WindowManager.LayoutParams mWindowParams;
  40. /**
  41. * At which position is the item currently being dragged. Note that this
  42. * takes in to account header items.
  43. */
  44. private int mDragPos;
  45. /**
  46. * At which position was the item being dragged originally
  47. */
  48. private int mSrcDragPos;
  49. private int mDragPointX;   // at what x offset inside the item did the user grab it
  50. private int mDragPointY;   // at what y offset inside the item did the user grab it
  51. private int mXOffset;  // the difference between screen coordinates and coordinates in this view
  52. private int mYOffset;  // the difference between screen coordinates and coordinates in this view
  53. private DragListener mDragListener;
  54. private DropListener mDropListener;
  55. private RemoveListener mRemoveListener;
  56. private int mUpperBound;
  57. private int mLowerBound;
  58. private int mHeight;
  59. private GestureDetector mGestureDetector;
  60. private static final int FLING = 0;
  61. private static final int SLIDE = 1;
  62. private static final int TRASH = 2;
  63. private int mRemoveMode = -1;
  64. private Rect mTempRect = new Rect();
  65. private Bitmap mDragBitmap;
  66. private final int mTouchSlop;
  67. private int mItemHeightNormal;
  68. private int mItemHeightExpanded;
  69. private int mItemHeightHalf;
  70. private Drawable mTrashcan;
  71. public DragAndDropListView(Context context, AttributeSet attrs) {
  72. super(context, attrs);
  73. SharedPreferences pref = context.getSharedPreferences(“Music”, 3);
  74. mRemoveMode = pref.getInt(“deletemode”, -1);
  75. mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  76. Resources res = getResources();
  77. mItemHeightNormal = res.getDimensionPixelSize(R.dimen.normal_height);
  78. mItemHeightHalf = mItemHeightNormal / 2;
  79. mItemHeightExpanded = res.getDimensionPixelSize(R.dimen.expanded_height);
  80. }
  81. @Override
  82. public boolean onInterceptTouchEvent(MotionEvent ev) {
  83. if (mRemoveListener != null && mGestureDetector == null) {
  84. if (mRemoveMode == FLING) {
  85. mGestureDetector = new GestureDetector(getContext(), new SimpleOnGestureListener() {
  86. @Override
  87. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  88. float velocityY) {
  89. if (mDragView != null) {
  90. if (velocityX > 1000) {
  91. Rect r = mTempRect;
  92. mDragView.getDrawingRect(r);
  93. if ( e2.getX() > r.right * 2 / 3) {
  94. // fast fling right with release near the right edge of the screen
  95. stopDragging();
  96. mRemoveListener.remove(mSrcDragPos);
  97. unExpandViews(true);
  98. }
  99. }
  100. // flinging while dragging should have no effect
  101. return true;
  102. }
  103. return false;
  104. }
  105. });
  106. }
  107. }
  108. if (mDragListener != null || mDropListener != null) {
  109. switch (ev.getAction()) {
  110. case MotionEvent.ACTION_DOWN:
  111. int x = (int) ev.getX();
  112. int y = (int) ev.getY();
  113. int itemnum = pointToPosition(x, y);
  114. if (itemnum == AdapterView.INVALID_POSITION) {
  115. break;
  116. }
  117. ViewGroup item = (ViewGroup) getChildAt(itemnum – getFirstVisiblePosition());
  118. mDragPointX = x – item.getLeft();
  119. mDragPointY = y – item.getTop();
  120. mXOffset = ((int)ev.getRawX()) – x;
  121. mYOffset = ((int)ev.getRawY()) – y;
  122. // The left side of the item is the grabber for dragging the item
  123. if (x < 64) {
  124. item.setDrawingCacheEnabled(true);
  125. // Create a copy of the drawing cache so that it does not get recycled
  126. // by the framework when the list tries to clean up memory
  127. Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
  128. startDragging(bitmap, x, y);
  129. mDragPos = itemnum;
  130. mSrcDragPos = mDragPos;
  131. mHeight = getHeight();
  132. int touchSlop = mTouchSlop;
  133. mUpperBound = Math.min(y – touchSlop, mHeight / 3);
  134. mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);
  135. return false;
  136. }
  137. stopDragging();
  138. break;
  139. }
  140. }
  141. return super.onInterceptTouchEvent(ev);
  142. }
  143. /*
  144. * pointToPosition() doesn’t consider invisible views, but we
  145. * need to, so implement a slightly different version.
  146. */
  147. private int myPointToPosition(int x, int y) {
  148. if (y < 0) {
  149. // when dragging off the top of the screen, calculate position
  150. // by going back from a visible item
  151. int pos = myPointToPosition(x, y + mItemHeightNormal);
  152. if (pos > 0) {
  153. return pos – 1;
  154. }
  155. }
  156. Rect frame = mTempRect;
  157. final int count = getChildCount();
  158. for (int i = count – 1; i >= 0; i–) {
  159. final View child = getChildAt(i);
  160. child.getHitRect(frame);
  161. if (frame.contains(x, y)) {
  162. return getFirstVisiblePosition() + i;
  163. }
  164. }
  165. return INVALID_POSITION;
  166. }
  167. private int getItemForPosition(int y) {
  168. int adjustedy = y – mDragPointY – mItemHeightHalf;
  169. int pos = myPointToPosition(0, adjustedy);
  170. if (pos >= 0) {
  171. if (pos <= mSrcDragPos) {
  172. pos += 1;
  173. }
  174. } else if (adjustedy < 0) {
  175. // this shouldn’t happen anymore now that myPointToPosition deals
  176. // with this situation
  177. pos = 0;
  178. }
  179. return pos;
  180. }
  181. private void adjustScrollBounds(int y) {
  182. if (y >= mHeight / 3) {
  183. mUpperBound = mHeight / 3;
  184. }
  185. if (y <= mHeight * 2 / 3) {
  186. mLowerBound = mHeight * 2 / 3;
  187. }
  188. }
  189. /*
  190. * Restore size and visibility for all listitems
  191. */
  192. private void unExpandViews(boolean deletion) {
  193. for (int i = 0;; i++) {
  194. View v = getChildAt(i);
  195. if (v == null) {
  196. if (deletion) {
  197. // HACK force update of mItemCount
  198. int position = getFirstVisiblePosition();
  199. int y = getChildAt(0).getTop();
  200. setAdapter(getAdapter());
  201. setSelectionFromTop(position, y);
  202. // end hack
  203. }
  204. try {
  205. layoutChildren(); // force children to be recreated where needed
  206. v = getChildAt(i);
  207. } catch (IllegalStateException ex) {
  208. // layoutChildren throws this sometimes, presumably because we’re
  209. // in the process of being torn down but are still getting touch
  210. // events
  211. }
  212. if (v == null) {
  213. return;
  214. }
  215. }
  216. ViewGroup.LayoutParams params = v.getLayoutParams();
  217. params.height = mItemHeightNormal;
  218. v.setLayoutParams(params);
  219. v.setVisibility(View.VISIBLE);
  220. }
  221. }
  222. /* Adjust visibility and size to make it appear as though
  223. * an item is being dragged around and other items are making
  224. * room for it:
  225. * If dropping the item would result in it still being in the
  226. * same place, then make the dragged listitem’s size normal,
  227. * but make the item invisible.
  228. * Otherwise, if the dragged listitem is still on screen, make
  229. * it as small as possible and expand the item below the insert
  230. * point.
  231. * If the dragged item is not on screen, only expand the item
  232. * below the current insertpoint.
  233. */
  234. private void doExpansion() {
  235. int childnum = mDragPos – getFirstVisiblePosition();
  236. if (mDragPos > mSrcDragPos) {
  237. childnum++;
  238. }
  239. int numheaders = getHeaderViewsCount();
  240. View first = getChildAt(mSrcDragPos – getFirstVisiblePosition());
  241. for (int i = 0;; i++) {
  242. View vv = getChildAt(i);
  243. if (vv == null) {
  244. break;
  245. }
  246. int height = mItemHeightNormal;
  247. int visibility = View.VISIBLE;
  248. if (mDragPos < numheaders && i == numheaders) {
  249. // dragging on top of the header item, so adjust the item below
  250. // instead
  251. if (vv.equals(first)) {
  252. visibility = View.INVISIBLE;
  253. } else {
  254. height = mItemHeightExpanded;
  255. }
  256. } else if (vv.equals(first)) {
  257. // processing the item that is being dragged
  258. if (mDragPos == mSrcDragPos || getPositionForView(vv) == getCount() – 1) {
  259. // hovering over the original location
  260. visibility = View.INVISIBLE;
  261. } else {
  262. // not hovering over it
  263. // Ideally the item would be completely gone, but neither
  264. // setting its size to 0 nor settings visibility to GONE
  265. // has the desired effect.
  266. height = 1;
  267. }
  268. } else if (i == childnum) {
  269. if (mDragPos >= numheaders && mDragPos < getCount() – 1) {
  270. height = mItemHeightExpanded;
  271. }
  272. }
  273. ViewGroup.LayoutParams params = vv.getLayoutParams();
  274. params.height = height;
  275. vv.setLayoutParams(params);
  276. vv.setVisibility(visibility);
  277. }
  278. }
  279. @Override
  280. public boolean onTouchEvent(MotionEvent ev) {
  281. if (mGestureDetector != null) {
  282. mGestureDetector.onTouchEvent(ev);
  283. }
  284. if ((mDragListener != null || mDropListener != null) && mDragView != null) {
  285. int action = ev.getAction();
  286. switch (action) {
  287. case MotionEvent.ACTION_UP:
  288. case MotionEvent.ACTION_CANCEL:
  289. Rect r = mTempRect;
  290. mDragView.getDrawingRect(r);
  291. stopDragging();
  292. if (mRemoveMode == SLIDE && ev.getX() > r.right * 3 / 4) {
  293. if (mRemoveListener != null) {
  294. mRemoveListener.remove(mSrcDragPos);
  295. }
  296. unExpandViews(true);
  297. } else {
  298. if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) {
  299. mDropListener.drop(mSrcDragPos, mDragPos);
  300. }
  301. unExpandViews(false);
  302. }
  303. break;
  304. case MotionEvent.ACTION_DOWN:
  305. case MotionEvent.ACTION_MOVE:
  306. int x = (int) ev.getX();
  307. int y = (int) ev.getY();
  308. dragView(x, y);
  309. int itemnum = getItemForPosition(y);
  310. if (itemnum >= 0) {
  311. if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) {
  312. if (mDragListener != null) {
  313. mDragListener.drag(mDragPos, itemnum);
  314. }
  315. mDragPos = itemnum;
  316. doExpansion();
  317. }
  318. int speed = 0;
  319. adjustScrollBounds(y);
  320. if (y > mLowerBound) {
  321. // scroll the list up a bit
  322. if (getLastVisiblePosition() < getCount() – 1) {
  323. speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
  324. } else {
  325. speed = 1;
  326. }
  327. } else if (y < mUpperBound) {
  328. // scroll the list down a bit
  329. speed = y < mUpperBound / 2 ? -16 : -4;
  330. if (getFirstVisiblePosition() == 0
  331. && getChildAt(0).getTop() >= getPaddingTop()) {
  332. // if we’re already at the top, don’t try to scroll, because
  333. // it causes the framework to do some extra drawing that messes
  334. // up our animation
  335. speed = 0;
  336. }
  337. }
  338. if (speed != 0) {
  339. smoothScrollBy(speed, 30);
  340. }
  341. }
  342. break;
  343. }
  344. return true;
  345. }
  346. return super.onTouchEvent(ev);
  347. }
  348. private void startDragging(Bitmap bm, int x, int y) {
  349. stopDragging();
  350. mWindowParams = new WindowManager.LayoutParams();
  351. mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
  352. mWindowParams.x = x – mDragPointX + mXOffset;
  353. mWindowParams.y = y – mDragPointY + mYOffset;
  354. mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
  355. mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
  356. mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  357. | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
  358. | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
  359. | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
  360. | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
  361. mWindowParams.format = PixelFormat.TRANSLUCENT;
  362. mWindowParams.windowAnimations = 0;
  363. Context context = getContext();
  364. ImageView v = new ImageView(context);
  365. v.setBackgroundResource(R.drawable.drag_and_drop_image);
  366. v.setPadding(0, 0, 0, 0);
  367. v.setImageBitmap(bm);
  368. mDragBitmap = bm;
  369. mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
  370. mWindowManager.addView(v, mWindowParams);
  371. mDragView = v;
  372. }
  373. private void dragView(int x, int y) {
  374. if (mRemoveMode == SLIDE) {
  375. float alpha = 1.0f;
  376. int width = mDragView.getWidth();
  377. if (x > width / 2) {
  378. alpha = ((float)(width – x)) / (width / 2);
  379. }
  380. mWindowParams.alpha = alpha;
  381. }
  382. if (mRemoveMode == FLING || mRemoveMode == TRASH) {
  383. mWindowParams.x = x – mDragPointX + mXOffset;
  384. } else {
  385. mWindowParams.x = 0;
  386. }
  387. mWindowParams.y = y – mDragPointY + mYOffset;
  388. mWindowManager.updateViewLayout(mDragView, mWindowParams);
  389. if (mTrashcan != null) {
  390. int width = mDragView.getWidth();
  391. if (y > getHeight() * 3 / 4) {
  392. mTrashcan.setLevel(2);
  393. } else if (width > 0 && x > width / 4) {
  394. mTrashcan.setLevel(1);
  395. } else {
  396. mTrashcan.setLevel(0);
  397. }
  398. }
  399. }
  400. private void stopDragging() {
  401. if (mDragView != null) {
  402. mDragView.setVisibility(GONE);
  403. WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
  404. wm.removeView(mDragView);
  405. mDragView.setImageDrawable(null);
  406. mDragView = null;
  407. }
  408. if (mDragBitmap != null) {
  409. mDragBitmap.recycle();
  410. mDragBitmap = null;
  411. }
  412. if (mTrashcan != null) {
  413. mTrashcan.setLevel(0);
  414. }
  415. }
  416. public void setTrashcan(Drawable trash) {
  417. mTrashcan = trash;
  418. mRemoveMode = TRASH;
  419. }
  420. public void setDragListener(DragListener l) {
  421. mDragListener = l;
  422. }
  423. public void setDropListener(DropListener l) {
  424. mDropListener = l;
  425. }
  426. public void setRemoveListener(RemoveListener l) {
  427. mRemoveListener = l;
  428. }
  429. public interface DragListener {
  430. void drag(int from, int to);
  431. }
  432. public interface DropListener {
  433. void drop(int from, int to);
  434. }
  435. public interface RemoveListener {
  436. void remove(int which);
  437. }
  438. }

发表评论