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
As mentioned above, layout is mainly responsible for item layout and space allocation. In this section, we will continue to look at the parent control Workspace of celllayout. The desktop of a mobile phone is composed of several screens, which you can slide at will. This layout is a workspace. Launcher's workspace is mainly responsible for processing sliding between multiple screens and adding wallpapers.
First, we know that draglayer includes the workspace and several celllayout. First, we should know how they perform their respective duties without affecting each other. This is the event transfer mechanism in Android. We know that in an application, the entire layout is a tree, so when a user's touch operation, such as a click event, how is the parent control on the outermost layer passed to a specific sub-space. This is due to the onintercepttouchevent and ontouchevent methods of view. The returned values of these two methods determine the time sequence of a touch event. Onintercepttouchevent, as its name implies, is an interception function. How are these two methods used to determine the event transfer?
1. If the user executes an action_down event and the onintercepttouchevent of the current view returns true, the event and subsequent action_move and action_up will not be passed to the view subcontrol, it is directly handled by the ontouchevent of the view.
2. If the above onintercepttouchevent returns false, then this event and the subsequent action_up, action_move will also be passed through onintercepttouchevent, and then passed to the onintercepttouchevent of the child control.
3. If the onintercepttouchevent of the view returns false, the event is passed to the target view, and the ontouchevent of the target view returns false, the event will continue to be passed to the ontouchevent at the upper level of the target view. If the ontouchevent method of the target view returns true, this event has been processed.
After learning about the touch event Transmission Mechanism in Android, it is easy to figure out How draglayer, workspace, and celllayout perform their respective jobs. Next, let's break into the workspace.
I. process sliding of multiple screens
We know that the workspace is composed of several celllayout horizontally tiled items. The simple point is that the actual layout is beyond the screen of the mobile phone, so you need to slide, A scroroller object is required to calculate the coordinates after each slide and to process the sliding state. With the launcher source code, you will find that many red forks are reported, most of which are caused by mscollx and mscolly errors. This is because these two attributes are not public and cannot be directly used by subclass, therefore, during implementation, we should note that when mscollx and mscrolly are used, getscrollx and getscrolly are used to call scrollby () when new values are assigned to mscrollx and mscrolly () or run the scrollto function.
1. Make several celllayout tiles. Because the size of each celllayout is the screen size of the mobile phone, it is very easy to tile horizontally here, you can directly call the layout method of each celllayout in onlayout for layout. You only need to control the left and right of each celllayout in order.
Protected void onlayout (Boolean changed, int L, int T, int R, int B) {final int COUNT = getchildcount (); int childleft = 0; // horizontally tiled celllayoutfor (INT I = 0; I <count; I ++) {view child = getchildat (I); Final int width = child. getmeasuredwidth (); Final int Height = child. getmeasuredheight (); If (child. getvisibility ()! = Gone) {Child. layout (childleft, 0, childleft + width, height); childleft + = width ;}}}
2. How to Handle screen sliding and screen sliding must be handled in the action_move event. We introduced Android's event interception mechanism at the beginning of the article. We wanted to let the sliding event be handled by the workspace without interfering with celllayout, naturally, some processing is required in onintercepttouchevent. First, we start with the onintercepttouchevent method. We can see the following code at a glance in the onintercepttouchevent method:
if(action == MotionEvent.ACTION_MOVE && mTouchState != TOUCH_STATE_STOPED){return true;}
At the same time, during ruturn, the returned message is mtouchstate! = Touch_state_stoped; that is to say, if the current slide, true is returned and the event is handed over to the ontouchevent event to process the sliding logic.
Then let's take a look at how the ontouchevent handles the action_move event:
Case motionevent. action_move:/*** here is the place for processing the slide. * Note that when the finger slides to the right, the screen slides to the left. **/If (mtouchstate = touch_state_scrolling) {final int xdiff = (INT) (mlastmotionx-x); mlastmotionx = x; // note that when mlastmotionx/*** is updated, scrollx value = last scrollx + xdiff */log. V (TAG, "Current scrollx size:" + getscrollx (); log. V (TAG, "current difference size:" + xdiff); // The following determines whether to move left or right IF (xdiff <0) {// the screen to the left log. V (TAG, "Current sliding to left"); If (getscrollx ()> 0) {// A/*** xdiff with a small difference is Negative, so * is similar to sliding to the right. When sliding to the left of the first screen, the absolute value of xdiff is larger than that of scrollx * at this time, the value of scrollx is close to 0, and the absolute value of xdiff is likely to be greater than 0. Therefore, the following restrictions are imposed: */INT xdelta = math. max (xdiff,-getscrollx (); scrollby (xdelta, 0) ;}} else if (xdiff> 0) {// log to the right of the screen. V (TAG, "sliding to the right"); Final int available = getchildat (getchildcount ()-1 ). getright (); log. V (TAG, "rightmost sliding:" + available); Final int availablesroll = available-getscrollx ()-getwidth (); log. V (TAG, "Maximum sliding distance currently:" + availablesroll); If (availablesroll> 0) {/*** note: ** when sliding the second to last screen, there may be xdiff> availablescoll * because the leftmost value of scrollx is * available-getwidth is the maximum value range of scrollx, availablesroll = m-the current sliding distance (scrollx); * in this way, you cannot slide to the right when you are on the last screen */scrollby (math. min (availablesroll, xdiff), 0) ;}} break;
Here, based on the new coordinates, even if it is left or right. Processing the slide operation at the same time. So when we stop, how does it work? View the processing logic in the action_up event:
If (mtouchstate = touch_state_scrolling) {final velocitytracker tracker = mvelocitytracker; tracker. computecurrentvelocity (1000); // use the pix/s unit int velx = (INT) tracker. getxvelocity (); log. V (TAG, "Current Sliding Speed:" + velx); If (velx> snap_velocity & mcurrentscreen> 0) {// left snaptoscreen (mCurrentScreen-1 );} else if (velx <-snap_velocity & mcurrentscreen <getchildcount ()-1) {// snaptoscreen (mcurrentscreen + 1) to the right;} else {// otherwise, to see which screen displays more, slide to which screen final int screenwidth = getwidth (); // analyze why final int whichscreen = (getscrollx () + screenwidth/2)/screenwidth;/*** is actually very simple. It is based on the current screen. If scrollx is more than half, slide the next screen * stay on the screen if there are no more than half *. Therefore, the idea of getscrollx () + screenwidth/2/screenwidth is * If scollx is more than half of the screen, the size of the half screen is divided by the size of the entire screen. * otherwise, the screen where scrollx is located */log. W (TAG, "Current srollx value:" + getscrollx (); snaptoscreen (whichscreen );}}
Note that velocitytracker is used to calculate the sliding speed, because when sliding the desktop, we should pay attention to the details. When moving the desktop quickly instead of dragging it, slide between screens to the next screen. This uses velocitytracker to calculate the sliding speed. If the sliding speed is greater than a certain value, it will directly slide to the next screen. The method snaptoscreen processes the specific screen to which the image slides. Let's take a look at the logic of this good method:
Private void snaptoscreen (INT screen) {log. W (TAG, "current screen:" + mcurrentscreen + "Slip screen:" + screen); enablechildrencache (); screen = math. max (0, math. min (screen, getchildcount ()-1); Boolean screenchange = screen! = Mcurrentscreen; mnextscreen = screen; view focusedchild = getfocusedchild (); If (focusedchild! = NULL & screenchange & focusedchild = getchildat (mcurrentscreen) {focusedchild. clearfocus (); // when switching the screen, remove the focus of the current screen} final int newx = screen * getwidth (); // final int scrollx = getscrollx (); // final int Delta = newx-scrollx; // deviation,> 0 to the right, <0 to the left mscroller. startscroll (scrollx, 0, Delta, 0, math. ABS (DELTA) * 2); log. W (TAG, "startscroll yes"); invalidate ();}
In this snaptoscreen method, the logic is very simple. It is mainly to call the startscroll method of scroroller and start the slide based on the current sliding position and target position. However, in this case, this method does not have any effect, because the startscroll method only starts to slide and does not constantly update data and process the moving tasks, these tasks are completed by the computescroll method. Next, let's go to the computescroll method to see its logic:
/*** This is the * method we call scrolltoscreen when mscroller slides to a screen. We call startscroll (). However, if you do not overwrite computescroll *, you will find that {@ link # snaptoscreen (INT)} has no effect, because * When we call startscroll while sliding, we only set the position where we want to slide, but * How to slide during the sliding process is still in this method. * When mscroller. computescroloffset returns true, indicating that the destination has not been slipped. * When the false result is returned, the calculation continues. * When the false result is returned, it indicates that the end point set by startscroll is sliding. ** Grandma wants to figure it out after half a day, hi, cup! * @ See # snaptoscreen (INT) */Public void computescroll () {If (mscroller. computescroloffset () {// mscroller. computescroloffset calculates the current position // returns true, indicating that scroll has not stopped int newx = mscroller. getcurrx (); int newy = mscroller. getcurry ();/*** in fact, scrollto is not used here. You only need to set the values of mscrollx and mscrolly to * mscroller. getcurrx () and mscroller. getcurry () is fine * but we cannot set it directly, so we use scrollto to complete */scrollto (newx, newy);/*** here we need to call postinvalidate. Otherwise, when sliding, You will find * The interface will be stuck in the middle of the two screens */postinvalidate (); log. V (TAG, "computescroll was called: The scrollx and scrolly are" + getscrollx () + "," + getscrolly ();} else if (mnextscreen! = Invalid_screen) {// scroll stops, then slide to the valid and appropriate screen mcurrentscreen = math. min (math. max (mnextscreen, 0), getchildcount ()-1); mnextscreen = invalid_screen; // mark mnextscreen as invalid. // uorderlauncher. setscreen (mcurrentscreen); // clear the sub-control to draw the cache log. V (TAG, "scroll is stop"); clearchildrencache ();}}
At this point, I think it is quite clear why the entire screen slides.
In the next article, we will continue to reveal how the wallpaper is added and how it is moved as the screen moves.