In Android development, ListView and GridView are often used. Sometimes the system ListView and GridView cannot meet our needs. Therefore, we need to define a ListView or GridView by ourselves, in my previous article, I used a custom example of sliding left and right to delete items. If you are interested, you can see that Android uses Scroller to achieve the brilliant effect of sliding left and right to delete items, today, this article will give you a custom control for the GridView. The GridView is mainly used to display grid controls, which are common in Android development. Compared with TextView, buttons are more complicated. Today, we bring you a long-pressed GridView item, and drag it onto other items to exchange the items in the GridView, A typical example is our Launcher. There are a lot of demos about the drag of the GridView on the Internet, but most of them are the same, and there are some bugs, and most of them are clicked on the items of the GridView and then dragged, or there is no real-time exchange between items. Today we will give you a more detailed introduction of the GridView drag and drop, and make the Demo more perfect and easier for everyone to accept, this article may be closed when many people hear that it is complicated to implement. In fact, it tells everyone that it is not complicated to understand the idea. If you don't believe it, let's take a look at it, first, let's talk about the Implementation ideas.
According to the X and Y coordinates of the finger pressed by the finger, we can use Handler and Runnable to implement a timer when pressing the item finger we clicked on the GridView. If the timer time is 1000 milliseconds, within 1000 milliseconds, if the finger is lifted and the timer is not lifted, and the finger is clicked in the area where the item of the GridView is located, it indicates that we have long-pressed the items of the GridView. If we have long-pressed the items, the items are hidden, then, use WindowManager to add an item image on the screen to replace the hidden item. When our fingers move on the screen, update the position of the item image, and then follow the X, Y coordinates can be used to obtain the position to move to the GridView. When there are too many items in the GridView, the screen may not be displayed. we drag the item image to the bottom of the screen with our fingers, to trigger the GridView to scroll up, similarly, when we drag the item image to the screen, trigger the GridView to scroll down to exchange data and refresh the interface, after reading the above ideas, have you found some feelings about removing the image of item? I want to try it. Okay, next, we will implement the drag-and-drop GridView based on our ideas. To create a new project, we will call it DragGridView.
Create a new class DragGridView to inherit the GridView. Let's take a look at the code of DragGridView, and then explain it according to the code.
Package com. example. draggridview; import android. annotation. suppressLint; 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; imp Ort android. view. windowManager; import android. widget. adapterView; import android. widget. gridView; import android. widget. imageView;/*** @ blog http://blog.csdn.net/xiaanming *** @ author xiaanming ***/@ SuppressLint (NewApi) public class DragGridView extends GridView {/*** DragGridView's item is long based on the response time, the default value is 1000 milliseconds. You can also set */private long dragResponseMS = 1000;/* Whether to drag or drop data. The default value is */private boolean isDrag = false; Private int mDownX; private int mDownY; private int moveX; private int moveY;/*** position being dragged */private int mDragPosition; /* View */private View mStartDragItemView = null for the item to be dragged;/* use an ImageView */private ImageView mDragImageView to drag the image; /* Vibrator */private Vibrator mVibrator; private WindowManager mWindowManager;/* layout parameters of the item image */private WindowManager. layoutParams mWindowLayoutPar Ams;/*** Bitmap */private Bitmap of the item to be dragged;/*** the distance from the point to the top edge of the item */private int mPoint2ItemTop; /*** the distance between the pressed point and the left edge of the item */private int mPoint2ItemLeft;/*** the offset of DragGridView from the top of the screen */private int mOffset2Top; /*** offset of DragGridView from the left of the screen */private int mOffset2Left;/*** height of the status bar */private int mStatusHeight; /*** automatically scroll down the DragGridView Boundary Value */private int mDownScrollBorder;/*** DragGridVie W: automatically scroll up the Boundary Value */private int mUpScrollBorder;/*** DragGridView auto-scroll speed */private static final int speed = 80; /*** interface for callback of item change */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); // get the height of the status bar} private Handler mHandler = new Handler (); // Runnableprivate Runnable mLongClickRunnable = new Runnable () {@ Overridepublic void run () {isDrag = true; // you can drag mVibrat Or. vibrate (50); // shake the mStartDragItemView. setVisibility (View. INVISIBLE); // hide this item // display the item image createDragImage (mDragBitmap, mDownX, mDownY) according to the point we press );}}; /*** set the callback interface * @ param onChanageListener */public void setOnChangeListener (OnChanageListener onChanageListener) {this. onChanageListener = onChanageListener;}/*** sets the number of milliseconds to drag a response. The default value is 1000 milliseconds * @ param dragResponseMS */public void setDragResponseMS (long dragRespons EMS) {this. dragResponseMS = dragResponseMS;} @ Overridepublic boolean dispatchTouchEvent (MotionEvent ev) {switch (ev. getAction () {case MotionEvent. ACTION_DOWN: // use Handler to delay dragResponseMS and execute mLongClickRunnablemHandler. postDelayed (mLongClickRunnable, dragResponseMS); mDownX = (int) ev. getX (); mDownY = (int) ev. getY (); // obtain the positionmDragPosition = pointToPosition (mDownX, mDownY) of the clicked item based on the X and Y coordinates of the pressed item; if (mDragPosi Tion = AdapterView. INVALID_POSITION) {return super. dispatchTouchEvent (ev);} // obtain the corresponding ViewmStartDragItemView = getChildAt (mDragPosition-getFirstVisiblePosition () of the item based on position ()); // you can refer to the figure above in my blog to understand mPoint2ItemTop = mDownY-mStartDragItemView. getTop (); mPoint2ItemLeft = mDownX-mStartDragItemView. getLeft (); mOffset2Top = (int) (ev. getRawY ()-mDownY); mOffset2Left = (int) (ev. getRawX ()-mDown X); // get the offset that DragGridView automatically scrolls up. If it is smaller than this value, DragGridView rolls down mDownScrollBorder = getHeight ()/4; // get the offset that DragGridView automatically scrolls down, if the value is greater than this value, DragGridView scrolls up to mUpScrollBorder = getHeight () * 3/4; // enables the mDragItemView plotting cache mStartDragItemView. setDrawingCacheEnabled (true); // gets the Bitmap object mDragBitmap = Bitmap in the cache. createBitmap (mStartDragItemView. getDrawingCache (); // this step is critical. Release the drawing cache to avoid repeated image mStartDragItemView. destroyDrawin GCache (); break; case MotionEvent. ACTION_MOVE: int moveX = (int) ev. getX (); int moveY = (int) ev. getY (); // if we move above the item that is pressed, we will not remove mRunnableif (! IsTouchInItem (mStartDragItemView, moveX, moveY) {mHandler. removeCallbacks (mLongClickRunnable);} break; case MotionEvent. ACTION_UP: mHandler. removeCallbacks (mLongClickRunnable); mHandler. removeCallbacks (mScrollRunnable); break;} return super. dispatchTouchEvent (ev );} /*** click ** @ param itemView * @ param x * @ param y * @ return */private boolean isTouchInItem (View dragView, int x, int y) {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;} @ Overridepublic boolean onTouchEvent (MotionEvent ev) {if (isDrag & mDragImageView! = Null) {switch (ev. getAction () {case MotionEvent. ACTION_MOVE: moveX = (int) ev. getX (); moveY = (int) ev. getY (); // drag itemonDragItem (moveX, moveY); break; case MotionEvent. ACTION_UP: onStopDrag (); isDrag = false; break;} return true;} return super. onTouchEvent (ev );} /*** create a dragged image * @ param bitmap * @ param downX * The X coordinate of the Point pressed relative to the parent control * @ param downY * The Point pressed relative to the X coordinate of the parent Control */private void createDragImage (Bitmap bitmap, int downX, Int downY) {mWindowLayoutParams = new WindowManager. layoutParams (); mWindowLayoutParams. format = PixelFormat. TRANSLUCENT; // transparent mWindowLayoutParams outside the image. gravity = Gravity. TOP | Gravity. LEFT; mWindowLayoutParams. x = downX-mPoint2ItemLeft + mOffset2Left; mWindowLayoutParams. y = downY-mPoint2ItemTop + mOffset2Top-mStatusHeight; mWindowLayoutParams. alpha = 0.55f; // transparency 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);}/*** move the image from the interface */Private void removeDragImage () {if (mDragImageView! = Null) {mWindowManager. removeView (mDragImageView); mDragImageView = null;}/*** drag an item to update the position of the item image, exchange of items and self-rolling of the 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, mWindowLayout Params); // update the Image Location onSwapItem (moveX, moveY); // The GridView automatically scrolls the mHandler. post (mScrollRunnable);}/*** when the moveY value is greater than the scroll up boundary value, the GridView is triggered to automatically scroll up * When the moveY value is smaller than the scroll down boundary value, automatically scroll down in violation of the GridView * otherwise do not scroll */private Runnable mScrollRunnable = new Runnable () {@ Overridepublic void run () {int scrollY; if (moveY> mUpScrollBorder) {scrollY =-speed; mHandler. postDelayed (mScrollRunnable, 25);} else if (moveY <mDownScrollBorder) {scr OllY = speed; mHandler. postDelayed (mScrollRunnable, 25);} else {scrollY = 0; mHandler. removeCallbacks (mScrollRunnable);} // when our fingers reach the offset of the up or down scroll of the GridView, our fingers may not move, but DragGridView is automatically scrolling // so here we call the onSwapItem () method to exchange itemonSwapItem (moveX, moveY); View view = getChildAt (mDragPosition-getFirstVisiblePosition ()); // implement automatic scrolling of the GridView smoothScrollToPositionFromTop (mDragPosition, view. getTop () + scrollY );}};/** * Swap items and control the display and hide effects between items * @ param moveX * @ param moveY */private void onSwapItem (int moveX, int moveY) {// obtain the positionint tempPosition = pointToPosition (moveX, moveY) of the item to which our fingers move; // If tempPosition is changed and tempPosition is not equal to-1, if (tempPosition! = MDragPosition & tempPosition! = AdapterView. INVALID_POSITION) {getChildAt (tempPosition-getFirstVisiblePosition ()). setVisibility (View. INVISIBLE); // drag the new item to hide getChildAt (mDragPosition-getFirstVisiblePosition ()). setVisibility (View. VISIBLE); // if (onChanageListener! = Null) {onChanageListener. onChange (mDragPosition, tempPosition);} mDragPosition = tempPosition;}/*** stop dragging. We will display the hidden items and remove the image */private void onStopDrag () {getChildAt (mDragPosition-getFirstVisiblePosition ()). setVisibility (View. VISIBLE); removeDragImage ();}/*** get the height of the status bar * @ param context * @ return */private static int getStatusHeight (Context context) {int statusHeight = 0; rect localRect = new Rect (); (Activity) context ). getWindow (). getDecorView (). getWindowVisibleDisplayFrame (localRect); statusHeight = localRect. top; if (0 = statusHeight) {Class
LocalClass; try {localClass = Class. forName (com. android. internal. r$ dimen); Object localObject = localClass. newInstance (); int i5 = Integer. parseInt (localClass. getField (status_bar_height ). get (localObject ). toString (); statusHeight = context. getResources (). getDimensionPixelSize (i5);} catch (Exception e) {e. printStackTrace () ;}} return statusHeight;}/***** @ author xiaanming **/public interface OnChanageListener {/*** callback method when the item is switched to a position, we only need to implement data exchange in this method to * @ param form * start position * @ param to * drag to position */public void onChange (int form, int );}}First, let's look at the event Distribution Method of DragGridView. If you don't know about Android event distribution, You can first understand it. Android event distribution is very important for custom controls. To put it simply, when we click the Item of DragGridView, we will first execute the dispatchTouchEvent () method to distribute the event, so we need to rewrite the dispatchTouchEvent () method to obtain the position of the item we press based on the pointToPosition () method when the finger is pressed, obtain the View corresponding to the position according to the getChildAt () method, and enable the timer of Long-pressed. The default time is 1000 milliseconds, if the finger is raised within 1000 milliseconds or the finger slides out of the item on the screen, cancel the long-pressed timer. Otherwise, it indicates that you can drag and drop the item, and the mobile phone will shake it, hide the long-pressed Item. Call the createDragImage () method on the screen to create the image of the long-pressed item. The image for creating the Item uses the WindowManager class, this class can create a form that is displayed on the Activity,
Before that, you must understand these distances. Before understanding these distances, you must first know the differences between getRawX (), getRawY (), getX (), and getY, getRawX (), getRawY () is the distance from the screen origin, while getX () and getY () are the distance from the point in the upper-left corner of the control, for the convenience of understanding, I used Word to draw a simple image. If the painting is not good, you will take a look at it. The red box is our GridView.
The distance between the mPoint2ItemTop point and the top edge of the Item, for example, the distance between the mPoint2ItemLeft point of line 1 and the left edge of the Item, for example, the distance between the upper edge of Line 2 mOffset2Top DragGridView and the upper edge of the screen, such as line 3, the height of the package's status bar, title bar, or layout on the DragGridView, this is very important. In reality, we need to use the mOffset2Left DragGridView left edge to the left edge of the screen, for example, line 4. The distance from my Demo is 0, because I set the DragGridView width to full screen, however, if the left edge of the screen is configured with a gap or there are other la s on the left, the mDownScrollBorder distance indicates that when there are too many items in the DragGridView, the screen display on the mobile phone is incomplete, when we drag the Item image to this height, DragGridV Iew automatically scroll down, such as line 5. mUpScrollBorder is the opposite of mDownScrollBorder. When we are above this height, DragGridView will automatically scroll up. For example, line 6 understands these six distances, let's take a look at the method used to create the Item image. I will not mention anything else. First, set format to PixelFormat. TRANSLUCENT indicates that the transparency is exclusive to the image and text, followed by the calculation of the distance between x and y. The coordinates in the upper left corner of the item are calculated, after understanding the preceding six distances, we can easily obtain the coordinates of x and y. However, you will find that the coordinates of y minus the height of the status bar, in addition, we need to obtain the cached Bitmap object of the item, and then set the Bitmap to an ImageView. Why? If the addView () method is called to add the item directly to WindowManager, an exception occurs because the item already has its own parent container DragGridView, here we use an ImageView to add items to WindowManager instead of items.
The above preparations have been completed to start dragging. To drag an image, we also need to rewrite the onTouchEvent () method to obtain the coordinates of the moving X and Y, the updateViewLayout method of WindowManager can be used to drag the image. For better user experience, we also need to exchange the real-time effect of items. We use the X, Y coordinate, use pointToPosition () to obtain the position to be dragged to, display the previous item, and hide the dragged item, this completes the item exchange on the interface, but I did not do the data exchange here, So I provided the callback interface OnChanageListener, we only need to implement the data exchange logic and refresh DragGridView. We also need to implement automatic upward or downward scrolling of DragGridView, and use Handler and mScrollRunnable to implement DragGridView scrolling using smoothScrollToPositionFromTop, you can refer to the Code for specific implementation.
Finger left the interface, remove the image of the item, and display the dragged item. This achieves the drag-and-drop effect of GirdView, next, let's use the custom drag-and-drop GridView. First, let's look at the layout of the main interface. Only one custom DragGridView is available.
Next, let's take a look at the layout of the items in DragGridView. The TextView under the above ImageView
After the layout is completed, let's take a look at the MainActivity code on the home page.
Package com. example. draggridview; import java. util. arrayList; import java. util. collections; import java. util. hashMap; import java. util. list; import android. app. activity; import android. OS. bundle; import android. widget. simpleAdapter; import com. example. draggridview. dragGridView. onChanageListener;/*** @ blog http://blog.csdn.net/xiaanming *** @ author xiaanming ***/public class MainActivity extends Activity {private List
> Canccelist = new ArrayList
> (); @ Overrideprotected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); DragGridView mDragGridView = (DragGridView) findViewById (R. id. dragGridView); for (int I = 0; I <30; I ++) {HashMap
ItemHashMap = new HashMap
(); ItemHashMap. put (item_image, R.drawable.com _ tencent_open_notice_msg_icon_big); itemHashMap. put (item_text, drag + Integer. toString (I); dataSourceList. add (itemHashMap);} final SimpleAdapter mSimpleAdapter = new SimpleAdapter (this, dataSourceList, R. layout. grid_item, new String [] {item_image, item_text}, new int [] {R. id. item_image, R. id. item_text}); mDragGridView. setAdapter (mSimpleAdapter); mDragGridView. setOnChangeListener (new OnChanageListener () {@ Overridepublic void onChange (int from, int to) {HashMap
Temp = celist. get (from); // Directly Interacts with item // dataSourceList. set (from, dataSourceList. get (to); // dataSourceList. set (to, temp); // dataSourceList. set (to, temp); // for processing here, pay attention to if (from <to) {for (int I = from; I
To) {for (int I = from; I> to; I --) {Collections. swap (dataSourceList, I, I-1) ;}} dataSourceList. set (to, temp); mSimpleAdapter. notifyDataSetChanged ();}});}}
The code here is relatively simple. We will mainly talk about the onChange () method. We will set a callback interface of OnChanageListener for the mDragGridView to implement the data exchange logic in the onChange () method, the first parameter from is the start position of the item, and the second parameter to is the drag position of the item. The switch logic I used at the beginning is
HashMap
Temp = celist. get (from); // Directly Interacts with item // dataSourceList. set (from, dataSourceList. get (to); // dataSourceList. set (to, temp );
After reading the drag-and-drop GridView of Netease news, the data exchange logic is changed to the following method.
To put it simply, the data exchange logic, for example, we drag position from 5 to 7, and the logic I comment out is to directly exchange the data between 5 and 7, the logic behind this is to move the data at Location 6 to Location 5, move the data at location 7 to Location 6, and then display 5 6-> 5, 7-> 6, 5-> 7. I don't know if you understand it. Next let's run the project. Before running the project, we should not forget to add the vibration permission to AndroidManifest. xml.
Well, today's explanation is over. The effect is pretty good. Do you think it is not that difficult to drag and drop the GridView after reading this article? Do you have your own idea in your mind? I suggest you click it and you can implement the drag-and-drop Implementation of ListView by yourself. ListView is simpler than the GridView, A good learning method is not to understand people's code, but to read the code and write it out based on their own ideas. Therefore, we encourage everyone to think more about the code. If they do not understand the code, leave a message below, I will answer your questions!
Project source code, click to download
I am honored to beCSDN 2013 blog Star SelectionHope to continue to get everyone's support and encouragement. If you see a friend, please vote for me!
Voting address: Http://vote.blog.csdn.net/blogstaritem/blogstar2013/xiaanming