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

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

  1. /*
    • Copyright (C) 2008 The Android Open Source Project
  2. *
    • Licensed under the Apache License, Version 2.0 (the “License”);
    • you may not use this file except in compliance with the License.
    • You may obtain a copy of the License at
  3. *
  4. *
    • Unless required by applicable law or agreed to in writing, software
    • distributed under the License is distributed on an “AS IS” BASIS,
    • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    • See the License for the specific language governing permissions and
    • limitations under the License.
  5. */
  6. package com.flora;
  7. import android.content.Context;
  8. import android.content.SharedPreferences;
  9. import android.content.res.Resources;
  10. import android.graphics.Bitmap;
  11. import android.graphics.PixelFormat;
  12. import android.graphics.Rect;
  13. import android.graphics.drawable.Drawable;
  14. import android.util.AttributeSet;
  15. import android.view.GestureDetector;
  16. import android.view.Gravity;
  17. import android.view.MotionEvent;
  18. import android.view.View;
  19. import android.view.ViewConfiguration;
  20. import android.view.ViewGroup;
  21. import android.view.WindowManager;
  22. import android.view.GestureDetector.SimpleOnGestureListener;
  23. import android.widget.AdapterView;
  24. import android.widget.ImageView;
  25. import android.widget.ListView;
  26. public class DragAndDropListView extends ListView {
  27. private ImageView mDragView;
  28. private WindowManager mWindowManager;
  29. private WindowManager.LayoutParams mWindowParams;
  30. /**
    • At which position is the item currently being dragged. Note that this
    • takes in to account header items.
  31. */
  32. private int mDragPos;
  33. /**
    • At which position was the item being dragged originally
  34. */
  35. private int mSrcDragPos;
  36. private int mDragPointX; // at what x offset inside the item did the user grab it
  37. private int mDragPointY; // at what y offset inside the item did the user grab it
  38. private int mXOffset; // the difference between screen coordinates and coordinates in this view
  39. private int mYOffset; // the difference between screen coordinates and coordinates in this view
  40. private DragListener mDragListener;
  41. private DropListener mDropListener;
  42. private RemoveListener mRemoveListener;
  43. private int mUpperBound;
  44. private int mLowerBound;
  45. private int mHeight;
  46. private GestureDetector mGestureDetector;
  47. private static final int FLING = 0;
  48. private static final int SLIDE = 1;
  49. private static final int TRASH = 2;
  50. private int mRemoveMode = -1;
  51. private Rect mTempRect = new Rect();
  52. private Bitmap mDragBitmap;
  53. private final int mTouchSlop;
  54. private int mItemHeightNormal;
  55. private int mItemHeightExpanded;
  56. private int mItemHeightHalf;
  57. private Drawable mTrashcan;
  58. public DragAndDropListView(Context context, AttributeSet attrs) {
  59. super(context, attrs);
  60. SharedPreferences pref = context.getSharedPreferences(“Music”, 3);
  61. mRemoveMode = pref.getInt(“deletemode”, -1);
  62. mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  63. Resources res = getResources();
  64. mItemHeightNormal = res.getDimensionPixelSize(R.dimen.normal_height);
  65. mItemHeightHalf = mItemHeightNormal / 2;
  66. mItemHeightExpanded = res.getDimensionPixelSize(R.dimen.expanded_height);
  67. }
  68. @Override
  69. public boolean onInterceptTouchEvent(MotionEvent ev) {
  70. if (mRemoveListener != null && mGestureDetector == null) {
  71. if (mRemoveMode == FLING) {
  72. mGestureDetector = new GestureDetector(getContext(), new SimpleOnGestureListener() {
  73. @Override
  74. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  75. float velocityY) {
  76. if (mDragView != null) {
  77. if (velocityX > 1000) {
  78. Rect r = mTempRect;
  79. mDragView.getDrawingRect(r);
  80. if ( e2.getX() > r.right * 2 / 3) {
  81. // fast fling right with release near the right edge of the screen
  82. stopDragging();
  83. mRemoveListener.remove(mSrcDragPos);
  84. unExpandViews(true);
  85. }
  86. }
  87. // flinging while dragging should have no effect
  88. return true;
  89. }
  90. return false;
  91. }
  92. });
  93. }
  94. }
  95. if (mDragListener != null || mDropListener != null) {
  96. switch (ev.getAction()) {
  97. case MotionEvent.ACTION_DOWN:
  98. int x = (int) ev.getX();
  99. int y = (int) ev.getY();

  100. int itemnum = pointToPosition(x, y);

  101. if (itemnum == AdapterView.INVALID_POSITION) {
  102. break;
  103. }
  104. ViewGroup item = (ViewGroup) getChildAt(itemnum - getFirstVisiblePosition());
  105. mDragPointX = x - item.getLeft();
  106. mDragPointY = y - item.getTop();
  107. mXOffset = ((int)ev.getRawX()) - x;
  108. mYOffset = ((int)ev.getRawY()) - y;
  109. // The left side of the item is the grabber for dragging the item
  110. if (x < 64) {
  111. item.setDrawingCacheEnabled(true);
  112. // Create a copy of the drawing cache so that it does not get recycled
  113. // by the framework when the list tries to clean up memory
  114. Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
  115. startDragging(bitmap, x, y);
  116. mDragPos = itemnum;
  117. mSrcDragPos = mDragPos;
  118. mHeight = getHeight();
  119. int touchSlop = mTouchSlop;
  120. mUpperBound = Math.min(y - touchSlop, mHeight / 3);
  121. mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);
  122. return false;
  123. }
  124. stopDragging();
  125. break;
  126. }
  127. }
  128. return super.onInterceptTouchEvent(ev);
  129. }
  130. /*
    • pointToPosition() doesn’t consider invisible views, but we
    • need to, so implement a slightly different version.
  131. */
  132. private int myPointToPosition(int x, int y) {
  133. if (y < 0) {
  134. // when dragging off the top of the screen, calculate position
  135. // by going back from a visible item
  136. int pos = myPointToPosition(x, y + mItemHeightNormal);
  137. if (pos > 0) {
  138. return pos - 1;
  139. }
  140. }
  141. Rect frame = mTempRect;
  142. final int count = getChildCount();
  143. for (int i = count - 1; i >= 0; i–) {
  144. final View child = getChildAt(i);
  145. child.getHitRect(frame);
  146. if (frame.contains(x, y)) {
  147. return getFirstVisiblePosition() + i;
  148. }
  149. }
  150. return INVALID_POSITION;
  151. }
  152. private int getItemForPosition(int y) {
  153. int adjustedy = y - mDragPointY - mItemHeightHalf;
  154. int pos = myPointToPosition(0, adjustedy);
  155. if (pos >= 0) {
  156. if (pos <= mSrcDragPos) {
  157. pos += 1;
  158. }
  159. } else if (adjustedy < 0) {
  160. // this shouldn’t happen anymore now that myPointToPosition deals
  161. // with this situation
  162. pos = 0;
  163. }
  164. return pos;
  165. }
  166. private void adjustScrollBounds(int y) {
  167. if (y >= mHeight / 3) {
  168. mUpperBound = mHeight / 3;
  169. }
  170. if (y <= mHeight * 2 / 3) {
  171. mLowerBound = mHeight * 2 / 3;
  172. }
  173. }
  174. /*
    • Restore size and visibility for all listitems
  175. */
  176. private void unExpandViews(boolean deletion) {
  177. for (int i = 0;; i++) {
  178. View v = getChildAt(i);
  179. if (v == null) {
  180. if (deletion) {
  181. // HACK force update of mItemCount
  182. int position = getFirstVisiblePosition();
  183. int y = getChildAt(0).getTop();
  184. setAdapter(getAdapter());
  185. setSelectionFromTop(position, y);
  186. // end hack
  187. }
  188. try {
  189. layoutChildren(); // force children to be recreated where needed
  190. v = getChildAt(i);
  191. } catch (IllegalStateException ex) {
  192. // layoutChildren throws this sometimes, presumably because we’re
  193. // in the process of being torn down but are still getting touch
  194. // events
  195. }
  196. if (v == null) {
  197. return;
  198. }
  199. }
  200. ViewGroup.LayoutParams params = v.getLayoutParams();
  201. params.height = mItemHeightNormal;
  202. v.setLayoutParams(params);
  203. v.setVisibility(View.VISIBLE);
  204. }
  205. }
  206. /* Adjust visibility and size to make it appear as though
    • an item is being dragged around and other items are making
    • room for it:
    • If dropping the item would result in it still being in the
    • same place, then make the dragged listitem’s size normal,
    • but make the item invisible.
    • Otherwise, if the dragged listitem is still on screen, make
    • it as small as possible and expand the item below the insert
    • point.
    • If the dragged item is not on screen, only expand the item
    • below the current insertpoint.
  207. */
  208. private void doExpansion() {
  209. int childnum = mDragPos - getFirstVisiblePosition();
  210. if (mDragPos > mSrcDragPos) {
  211. childnum++;
  212. }
  213. int numheaders = getHeaderViewsCount();
  214. View first = getChildAt(mSrcDragPos - getFirstVisiblePosition());
  215. for (int i = 0;; i++) {
  216. View vv = getChildAt(i);
  217. if (vv == null) {
  218. break;
  219. }
  220. int height = mItemHeightNormal;
  221. int visibility = View.VISIBLE;
  222. if (mDragPos < numheaders && i == numheaders) {
  223. // dragging on top of the header item, so adjust the item below
  224. // instead
  225. if (vv.equals(first)) {
  226. visibility = View.INVISIBLE;
  227. } else {
  228. height = mItemHeightExpanded;
  229. }
  230. } else if (vv.equals(first)) {
  231. // processing the item that is being dragged
  232. if (mDragPos == mSrcDragPos || getPositionForView(vv) == getCount() - 1) {
  233. // hovering over the original location
  234. visibility = View.INVISIBLE;
  235. } else {
  236. // not hovering over it
  237. // Ideally the item would be completely gone, but neither
  238. // setting its size to 0 nor settings visibility to GONE
  239. // has the desired effect.
  240. height = 1;
  241. }
  242. } else if (i == childnum) {
  243. if (mDragPos >= numheaders && mDragPos < getCount() - 1) {
  244. height = mItemHeightExpanded;
  245. }
  246. }
  247. ViewGroup.LayoutParams params = vv.getLayoutParams();
  248. params.height = height;
  249. vv.setLayoutParams(params);
  250. vv.setVisibility(visibility);
  251. }
  252. }
  253. @Override
  254. public boolean onTouchEvent(MotionEvent ev) {
  255. if (mGestureDetector != null) {
  256. mGestureDetector.onTouchEvent(ev);
  257. }
  258. if ((mDragListener != null || mDropListener != null) && mDragView != null) {
  259. int action = ev.getAction();
  260. switch (action) {
  261. case MotionEvent.ACTION_UP:
  262. case MotionEvent.ACTION_CANCEL:
  263. Rect r = mTempRect;
  264. mDragView.getDrawingRect(r);
  265. stopDragging();
  266. if (mRemoveMode == SLIDE && ev.getX() > r.right * 3 / 4) {
  267. if (mRemoveListener != null) {
  268. mRemoveListener.remove(mSrcDragPos);
  269. }
  270. unExpandViews(true);
  271. } else {
  272. if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) {
  273. mDropListener.drop(mSrcDragPos, mDragPos);
  274. }
  275. unExpandViews(false);
  276. }
  277. break;
  278. case MotionEvent.ACTION_DOWN:
  279. case MotionEvent.ACTION_MOVE:
  280. int x = (int) ev.getX();
  281. int y = (int) ev.getY();
  282. dragView(x, y);
  283. int itemnum = getItemForPosition(y);
  284. if (itemnum >= 0) {
  285. if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) {
  286. if (mDragListener != null) {
  287. mDragListener.drag(mDragPos, itemnum);
  288. }
  289. mDragPos = itemnum;
  290. doExpansion();
  291. }
  292. int speed = 0;
  293. adjustScrollBounds(y);
  294. if (y > mLowerBound) {
  295. // scroll the list up a bit
  296. if (getLastVisiblePosition() < getCount() - 1) {
  297. speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
  298. } else {
  299. speed = 1;
  300. }
  301. } else if (y < mUpperBound) {
  302. // scroll the list down a bit
  303. speed = y < mUpperBound / 2 ? -16 : -4;
  304. if (getFirstVisiblePosition() == 0
  305. && getChildAt(0).getTop() >= getPaddingTop()) {
  306. // if we’re already at the top, don’t try to scroll, because
  307. // it causes the framework to do some extra drawing that messes
  308. // up our animation
  309. speed = 0;
  310. }
  311. }
  312. if (speed != 0) {
  313. smoothScrollBy(speed, 30);
  314. }
  315. }
  316. break;
  317. }
  318. return true;
  319. }
  320. return super.onTouchEvent(ev);
  321. }
  322. private void startDragging(Bitmap bm, int x, int y) {
  323. stopDragging();
  324. mWindowParams = new WindowManager.LayoutParams();
  325. mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
  326. mWindowParams.x = x - mDragPointX + mXOffset;
  327. mWindowParams.y = y - mDragPointY + mYOffset;
  328. mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
  329. mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
  330. mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  331. | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
  332. | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
  333. | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
  334. | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
  335. mWindowParams.format = PixelFormat.TRANSLUCENT;
  336. mWindowParams.windowAnimations = 0;
  337. Context context = getContext();
  338. ImageView v = new ImageView(context);
  339. v.setBackgroundResource(R.drawable.drag_and_drop_image);
  340. v.setPadding(0, 0, 0, 0);
  341. v.setImageBitmap(bm);
  342. mDragBitmap = bm;
  343. mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
  344. mWindowManager.addView(v, mWindowParams);
  345. mDragView = v;
  346. }
  347. private void dragView(int x, int y) {
  348. if (mRemoveMode == SLIDE) {
  349. float alpha = 1.0f;
  350. int width = mDragView.getWidth();
  351. if (x > width / 2) {
  352. alpha = ((float)(width - x)) / (width / 2);
  353. }
  354. mWindowParams.alpha = alpha;
  355. }
  356. if (mRemoveMode == FLING || mRemoveMode == TRASH) {
  357. mWindowParams.x = x - mDragPointX + mXOffset;
  358. } else {
  359. mWindowParams.x = 0;
  360. }
  361. mWindowParams.y = y - mDragPointY + mYOffset;
  362. mWindowManager.updateViewLayout(mDragView, mWindowParams);
  363. if (mTrashcan != null) {
  364. int width = mDragView.getWidth();
  365. if (y > getHeight() * 3 / 4) {
  366. mTrashcan.setLevel(2);
  367. } else if (width > 0 && x > width / 4) {
  368. mTrashcan.setLevel(1);
  369. } else {
  370. mTrashcan.setLevel(0);
  371. }
  372. }
  373. }
  374. private void stopDragging() {
  375. if (mDragView != null) {
  376. mDragView.setVisibility(GONE);
  377. WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
  378. wm.removeView(mDragView);
  379. mDragView.setImageDrawable(null);
  380. mDragView = null;
  381. }
  382. if (mDragBitmap != null) {
  383. mDragBitmap.recycle();
  384. mDragBitmap = null;
  385. }
  386. if (mTrashcan != null) {
  387. mTrashcan.setLevel(0);
  388. }
  389. }
  390. public void setTrashcan(Drawable trash) {
  391. mTrashcan = trash;
  392. mRemoveMode = TRASH;
  393. }
  394. public void setDragListener(DragListener l) {
  395. mDragListener = l;
  396. }
  397. public void setDropListener(DropListener l) {
  398. mDropListener = l;
  399. }
  400. public void setRemoveListener(RemoveListener l) {
  401. mRemoveListener = l;
  402. }
  403. public interface DragListener {
  404. void drag(int from, int to);
  405. }
  406. public interface DropListener {
  407. void drop(int from, int to);
  408. }
  409. public interface RemoveListener {
  410. void remove(int which);
  411. }
  412. }