Let's talk about the story behind the android desktop (launcher application) (8)-Let the desktop genie get through

Source: Internet
Author: User

Blog migration-I have migrated my blog to www.ijavaboy.com to better manage it. We are sorry for the inconvenience caused by no updates! New address of this article: Click me 



With the previous work, the desktop is basically ready to use, but it is far from the launcher that comes with the system. Among them, the items on the system launcher's desktop can be freely crossed (moved. At the same time, you can kill the traversal. In this article, let's take a look at how the genie on the desktop can realize their dream of crossing ....

The system launcher has made great effort to implement the drag and drop of items. To implement the drag-and-drop function, it defines a set of abstract concepts: dragsource, dragtarget, and dragcontroller ); and draglayer ). Dragsource is used to indicate the locations where items on the desktop can be dragged. Here, "where" is identified by dragsource. For example, in the system launcher, workspace, all‑gridview, and folder are all dragsource, that is, items in these locations can be dragged. Dragtarget corresponds to the position that can hold an item. For example, in the system launcher's workspace, folder, deletezone is dragtarget, that is, we can drag an item to put it down in these locations, and the item will be moved to this location. At the same time, in order to process the drag between multiple desktops, it defines a dragscroroller to process the logic of the drag between multiple desktops. An item has a life cycle on the desktop. Naturally, when you drag an item, a certain area under the desktop is fixed to the delete area (deletezone, when the dragged item is released in this region, it means that its life ends on the desktop.

With this basic understanding, it is no longer necessary to go into the code and look for more detailed answers. Next, let's continue to implement our unfinished functions:

We know that in the previous section, all actions start with a blank area on the user's long-pressed desktop. So what should we do when the user is long-pressed by an item on the desktop? Yes, that is, drag. Therefore, in the onlongclick event in the launcher, we need to add a long press to process an item. The complete onlongclick method is as follows:

/*** In this method, you need to determine whether the current long-pressed event is in a blank area or an item */@ overridepublic Boolean onlongclick (view v) {// activityutils. alert (getapplication (), "Long press"); If (! (V instanceof uordercelllayout) {v = (View) v. getparent (); // if the item is currently clicked, obtain its parent control, that is, uordercelllayout} cellinfo = (cellinfo) v. gettag (); // obtain the cellinfo information here. If (cellinfo = NULL) {log. V (TAG, "cellinfo is null"); Return true;} // log. V (TAG, "" + cellinfo. tostring ();/*** Note: When we obtain information about the current position in celllayout, we determine whether the current position is an item. If yes, save * item in cellinfo. view */If (cellinfo. view = NULL) {// It indicates a blank area log. V (TAG, "onlongclick, cellinfo. valid: "+ cellinfo. valid); If (cellinfo. valid) {// if the region is valid, addcellinfo = cellinfo; showpassworddialog (request_code_setup, null);} else {// process the drag-and-drop mworkspace. startdrag (cellinfo);} return true ;}

 

In this method, when an item is long pressed by the current user, mworkspace. startdrag (cellinfo) is executed. Therefore, let's go to this method and check its processing logic:

/*** Start to drag. This method does not really process the drag action. Instead, it is delivered to dragcontroller to complete * @ Param cellinfo */Public void startdrag (uordercelllayout. cellinfo) {view child = cellinfo. view; uordercelllayout layout = (uordercelllayout) getchildat (mcurrentscreen); layout. ondragchild (child); cellinfo. screen = mcurrentscreen; // start and drag the mdragcontroller. startdrag (this, child, cellinfo); invalidate ();}

 

In this method, only some auxiliary processing is done. The real implementer of the dragcontroller interface is actually implemented. Here, the draglayer control is the parent control of the workspace layout control. Next, let's go to the draglayer to find the secret of moving items:

Because draglayer implements the dragcontroller interface, let's start from the last interrupted point and look at the logic in startdrag. In this method, the input method is blocked and a mobile bitmap is created, at the same time, the system zooms in multiple times and sets relevant animation parameters. The complete code is as follows:

Public void startdrag (uorderworkspace workspace, view V, cellinfo) {log. V (TAG, "start dragging... "); // hide the input method if (minputmanager = NULL) {minputmanager = (inputmethodmanager) getcontext (). getsystemservice (context. input_method_service);} minputmanager. hidesoftinputfromwindow (getwindowtoken (), 0); If (mdraglistener! = NULL) {mdraglistener. ondragstart ();} log. W (TAG, "scrollx, scrolly:" + v. getscrollx () + "," + v. getscrolly (); mdragrect. set (v. getscrollx (), V. getscrolly (), 0, 0); offsetdescendantrecttomycoords (v, mdragrect ); // move the child's coordinate system to the parent coordinate system // mlastmotionx here is the mtouchoffsetx = (INT) (mlastmotionx-mdragrect obtained in onintercepttouchevent. left); mtouchoffsety = (INT) (mlastmotiony-mdragrect. top); V. clearfocus (); V. setpressed (false);/*** the bitmap corresponding to the view needs to be created below, during the movement process, the object you drag is a bitmap object * // you can specify Boolean willnotcache = v. willnotcachedrawing (); V. setwillnotcachedrawing (false); V. builddrawingcache (); Bitmap bitmap = v. getdrawingcache (); int width = bitmap. getwidth (); int Height = bitmap. getheight ();/*** we know that in launcher, when we hold down an item, an animation first pops up and scales it up. * here, is to calculate the magnification, and draw bitmap */matrix = new matrix () according to the magnification; float scalefactor = v. getwidth (); // calculate the scaling ratio scalefactor = (scalefactor + scale_size)/scalefactor; matrix. setscale (scalefactor, scalefactor); // create a bitmapmdragbitmap = Bitmap Based on the specified scaling ratio. createbitmap (bitmap, 0, 0, width, height, matrix, true); // clear the rendering cache v. destroydrawingcache (); V. setwillnotcachedrawing (willnotcache); // calculate the scaled bitmap and view offsetmbitmapoffsetx = (mdragbitmap. getwidth ()-width)/2; mbitmapoffsety = (mdragbitmap. getheight ()-height)/2; // Save the original item and hide moriginator = V; V. setvisibility (view. gone); // specify the starting animation Size Based on scalefactor/*** this is mainly used to make the items on the desktop have a transitional animation effect when they are played, avoid abrupt * set the initial value here. process the animation logic in dispatchdraw */manimationto = 1.0f; manimationfrom = 1.0f/scalefactor; manimationstate = running; manimationtype = running; manimationduration = running; mdragging = true; mdrawbitmaptpaint = mpaint; mworkspace = workspace; mcellinfo = cellinfo; invalidate ();}

 

By now, if you press an item on the desktop, the principle of item enlargement is clear at a glance. So how does it transition from the original size to the enlarged bitmap? So let's go to dispatchdraw to find out the truth. In this method, the animation effect of this transition is actually to set an animation time, and then the original size gradually changes to the enlarged size within such a long time. The complete code is as follows:

/*** In this method, we handle a transitional effect of the item size from the original size to the enlarged size */protected void dispatchdraw (canvas) {super. dispatchdraw (canvas); If (mdragging & mdragbitmap! = NULL) {If (manimationstate = animation_state_starting) {/*** animation start, write down the current time */manimationstate = animation_state_running; manimationstarttime = systemclock. uptimemillis ();} int left = (INT) (mscrollx + callback-mtouchoffsetx-mbitmapoffsetx); int Top = (INT) (mscrolly + mlastmotiony-mtouchoffsety-mbitmapoffsety ); if (manimationstate = animation_state_running) {// calculate the number of Float normalized = (float) (systemclock. uptimemillis ()-manimationstarttime)/manimationduration; If (normalized> = 1.0f) {manimationstate = animation_state_done;} normalized = math. min (normalized, 1.0f); float value = manimationfrom + (manimationto-manimationfrom) * future; if (manimationtype = Future) {// scale the current mdragbitmapint width = mdragbitmap. getwidth (); int Height = mdragbitmap. getheight (); canvas. save (); canvas. translate (left, top); canvas. translate (width * (1.0f-value), height * (1.0f-value); canvas. scale (value, value); canvas. drawbitmap (mdragbitmap, 0.0f, 0.0f, mdrawbitmaptpaint); // because the translate is done, it is 0.0canvas.restore () ;}} else {// otherwise it starts or stops, the canvas is drawn according to the current image. drawbitmap (mdragbitmap, left, top, mdrawbitmaptpaint );}}}

 

The previous steps are just preparations for drag and drop. Next, let's take a look at how drag is implemented. After analyzing the previous series of articles, you should have a natural intuition: find the answer in ontouchevent. Well, to handle drag and drop operations, it is nothing more than processing events such as motion_move. Here, we redraw the moving area and determine whether the current item is within 20 dip at the edge of the screen. If yes, we need to move it to the next screen. At the same time, you also need to determine whether the position of the current item is the deleted area. If it is the deleted area, add a color-changing mask to the paint brush that draws the bitmap, that is, we can see that when we drag an item through the bin area, the captured item becomes red. Next, let's take a look at the complete logic of this method:

Public Boolean ontouchevent (motionevent event) {If (! Mdragging) {log. V (TAG, "drag stopped, passed to child"); Return false;} final int action = event. getaction (); Final float x = event. getx (); Final float y = event. gety (); Switch (Action) {Case motionevent. action_down: mlastmotionx = x; mlastmotiony = y;/*** here, we need to determine that * we stipulate that the non-drag area is not within 20 dip on the left and right of the screen, in this way, when the item stays at the edge, * indicates that the item needs to be moved to the next screen. Of course, if there is a screen in this direction */If (x <non_scroll_zone | x> getwidth () -non_scroll_zone) {log. V (TAG, "sliding to edge"); mscroll State = scroll_state_waiting_outsize; If (x <non_scroll_zone) {scrollaction. setdirection (direction_left);} else {scrollaction. setdirection (direction_right);} postdelayed (scrollaction, scroll_delay);} else {mscrollstate = scroll_state_in_zone;} break; Case motionevent. action_move: Final int scrollx = getscrollx (); Final int scrolly = getscrolly (); Final int touchoffsetx = mtouchoffsetx; Final int touchoffsety = m Touchoffsety; Final int bitmapoffsetx = mbitmapoffsetx; Final int bitmapoffsety = mbitmapoffsety; // calculate the last position int left = (INT) (scrollx + vertex-touchoffsetx-bitmapoffsetx ); int Top = (INT) (scrolly + mlastmotiony-touchoffsety-bitmapoffsety); Final int width = mdragbitmap. getwidth (); Final int Height = mdragbitmap. getheight (); // The information of the previous location, mdragrect. set (left-1, top-1, width + Left + 1, height + TOP + 1); mlastm Otionx = x; margin = y; left = (INT) (scrollx + mlastmotionx-touchoffsetx-bitmapoffsetx); Top = (INT) (scrolly + mlastmotiony-touchoffsety-bitmapoffsety ); // set a new location and add all the drag-and-drop areas together with the mdragrect. union (left-1, top-1, width + Left + 1, height + TOP + 1); // check whether the currently dragged item is in the bin area if (mdraglistener! = NULL) {rect = new rect (); int rawx = (INT) (callback-touchoffsetx-bitmapoffsetx); int rawy = (INT) (mlastmotiony-touchoffsety-bitmapoffsety ); rect. set (rawx, rawy, rawx + width, rawy + height); Boolean indeletezone = mdraglistener. ondragmove (rect);/*** if it is in the delete area, a color change mask must be added to the color of the dragged item, and the color changes to red in the system launcher */If (indeletezone) {mdrawbitmaptpaint = mtrashpaint;} else {mdrawbitmaptpaint = mpaint;} // request to re-draw the drag-and-drop area invalidate (mdragrect);/*** determine whether it is on the edge. If yes, slide to the next screen */If (x <non_scroll_zone) {// process if (mscrollstate = scroll_state_in_zone) when the current status slides from the sliding area to the edge) {mscrollstate = scroll_state_waiting_outsize; scrollaction. setdirection (direction_left); // a certain time delay, that is, when we see the edge, it pauses for a while and then slides to the next screen postdelayed (scrollaction, scroll_delay );}} else if (x> getwidth ()-non_scroll_zone) {If (mscrollstate = scroll_state_in_zone) {log. W (TAG, "sliding to the next screen"); mscrollstate = scroll_state_waiting_outsize; scrollaction. setdirection (direction_right); postdelayed (scrollaction, scroll_delay) ;}} else {mscrollstate = scroll_state_in_zone;} break; Case motionevent. action_up: removecallbacks (scrollaction); // stopdrag (); int rawx = (INT) (mlastmotionx-mtouchoffsetx-mbitmapoffsetx); int rawy = (INT) (mlastmotiony-mtouchoffsety-mbitmapoffsety); rect = new rect (); rect. set (rawx, rawy, rawx + mdragbitmap. getwidth (), rawy + mdragbitmap. getheight (); stopdrag (INT) x, (INT) y, rect); break; default: break;} return true ;}

 

In the above Code, the moving process is processed in the move event. In the code, we can see that when an item is dragged to a position within 20 dip on the edge, the execution logic is:

scrollAction.setDirection(DIRECTION_RIGHT);postDelayed(scrollAction, SCROLL_DELAY);

Scollaction is a thread that slides from one screen to another. It should be noted that the slide you see to the next screen is not the one that slides the item to the next screen while dragging it, the next screen slides to the current phone window. Therefore, this logic should not be implemented by draglayer, but by workspace. Therefore, workspace implements the dragscroller interface, which is responsible for processing the logic of sliding an item to the next screen. The code of the thread class corresponding to scrollaction is as follows:

class ScrollRunnable implements Runnable{private int mDirection;@Overridepublic void run() {if(mDragScroller != null){if(mDirection == DIRECTION_LEFT){mDragScroller.scrollLeft();}else{mDragScroller.scrollRight();}}}public void setDirection(int dir){this.mDirection = dir;}}

 

It determines the direction of sliding to the next screen based on the position and direction calculated in action_move. As mentioned above, the current sliding behavior is that the next screen is displayed in the current mobile phone window, rather than the current item is actually dragged to the next screen. Therefore, we need to find the real-time provider of dragscroller: workspace. In workspace, it executes the scrollleft and scrollright logic:

/** * {@inheritDoc} */@Overridepublic void scrollLeft() {if(mCurrentScreen != INVALID_SCREEN && mCurrentScreen > 0 && mScroller.isFinished()){snapToScreen(mCurrentScreen-1);}}/** * {@inheritDoc} */@Overridepublic void scrollRight() {if(mCurrentScreen != INVALID_SCREEN && mCurrentScreen < getChildCount()-1 && mScroller.isFinished()){snapToScreen(mCurrentScreen+1);}}

 

You must remember the snaptoscreen method in the previous Workspace Analysis. If you do not remember it, you can go there to find the answer. Now, the drag process is implemented in this way. So, when we stop dragging and releasing the item, how does it be added to or deleted? The stopdrag method is called in the above action_up section. Let's look at the logic of this method. In fact, it is easy to determine whether the current location is the deleted region. If it is the deletion logic, if not, the logic of moving the location is executed. The Code is as follows:

Public void stopdrag (int x, int y, rect dragrect) {If (mdragging) {mdragging = false; If (mdragbitmap! = NULL) {mdragbitmap. recycle ();} uordercelllayout screen = mworkspace. getcurrentcelllayout (); log. E (TAG, "current screen is:" + mworkspace. getcurrentscreen (); log. E (TAG, "the screen of the moved item is" + mcellinfo. screen); If (mdraglistener! = NULL) {Boolean indeletezone = mdraglistener. ondragstop (dragrect); If (indeletezone) {// Delete this itemscreen. removeview (moriginator); invalidate (); iteminfo info = (iteminfo) moriginator. gettag (); uorderdbutils. deleteitemfromdb (getcontext (), Info); Return ;}// convert the current X and Y coordinates to the final int [] cellxy = new int [2] Cell in the current screen; screen. pointtocellexact (X, Y, cellxy); Final cellinfo = mcellinfo; cellinfo. cellx = cellxy [0] ; Cellinfo. celly = cellxy [1]; cellinfo. cellhspan = 1; cellinfo. cellvspan = 1; log. E (TAG, "cell after moving:" + cellxy [0] + "," + cellxy [1]); // call the Workspace Method, to move the current mworkspace. moveshortcutondesk (moriginator, cellinfo); If (moriginator! = NULL) {moriginator. setvisibility (view. Visible) ;}} invalidate ();}

 

The above code is very simple, that is, to determine whether it is in the delete region, if it is not to call mworkspace. moveshortcutondesk (moriginator, cellinfo); Method to complete the movement of the location. So let's take a look at this method. The logic in this method is divided into two situations. If it is moving in the same screen, you can simply redraw it based on the new location, however, if you are not on the same screen before and after moving, you need to delete the original screen and add it to the new screen. The Code is as follows:

/*** Drag an item long to move it. When it is stopped, it is based on its latest position, display it in the latest location * and update the database * @ Param v * @ Param cellinfo */Public void moveshortcutondesk (view V, cellinfo) {int screen = cellinfo. screen;/*** there are two situations to process. * 1. If you move in the same celllayout, it is very easy to directly set a new location, then let the celllayout be re-drawn. * 2. If you move it to another celllayout, you must first Delete the view from the original celllayout, then add it to * celllayout of the current screen */If (screen! = Mcurrentscreen) {uordercelllayout lastscreen = getcelllayout (screen); lastscreen. removeview (V); this. addinscreen (v, mcurrentscreen, cellinfo. cellx, cellinfo. celly, cellinfo. cellhspan, cellinfo. cellvspan, false);} else {uordercelllayout group = getcurrentcelllayout (); uordercelllayout. layoutparams Lp = (uordercelllayout. layoutparams) v. getlayoutparams (); LP. cellx = cellinfo. cellx; LP. celly = cellinfo. celly; LP. cellhspan = cellinfo. cellhspan; LP. cellvspan = cellinfo. cellvspan; group. invalidate ();}/*** at the same time, you need to synchronize the new location after moving to the database */iteminfo item = (iteminfo) v. gettag (); item. cellx = cellinfo. cellx; item. celly = cellinfo. celly; item. spanx = cellinfo. cellhspan; item. spany = cellinfo. cellvspan; item. screen = mcurrentscreen; uorderdbutils. moveitemindb (getcontext (), item );}

 

If you are not clear about the drag and drop details, you can refer to the previous two articles about drag and drop, which can help you understand the details of drag and drop.

Here, the complete drag-and-drop process of item is complete. Our desktop is getting closer and closer to the system launcher. However, we now only support adding application and shortcut to the desktop, which is the simplest item that occupies only one cell. You cannot add various widgets, folders, and other functions to the desktop. But don't worry. Everything comes in a sequential order, from simple to complex.

Next we will introduce how to make our desktop support various widgets ).

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.