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
Celllayout is designed to store controls of different sizes. To better control the addition and deletion of items, select to directly inherit viewgroup to implement this control.
When we press the desktop for a long time, there are two situations: one is that we press an item, and the other is that we press an empty position. Here, there is a problem.
1. How do I know whether the current position is a blank area or item?
2. Even if I know the coordinates of the current position, how can I know which cell the coordinates belong?
3. If the above two problems are solved, how can I add this item to the specified cell when I select an item to be added, how can we allocate suitable space based on the size of the current item?
To deal with the space occupied by cells and items, celllayout layout as shown in the following figure:
Next let's take a look at how the above cellinfo is represented in celllayout:
Public static final class cellinfo implements contextmenu. contextmenuinfo {public view; // viewpublic int cellx corresponding to the current item; // The starting cell public int celly in the horizontal direction of the item; // The Start Cell of the item in the vertical direction public int cellhspan; // The number of cells occupied by the item in the horizontal direction public int cellvspan; // The number of cells in the vertical direction of the item public Boolean valid; // whether the public int screen is valid; // The Screen rect current = new rect (); // used to Recursively search for continuous cells. The final arraylist <vacantcell> vacantcells = new arraylist <uordercelllayout of the current continuous area. cellinfo. vacantcell> (); Public void clear () {final arraylist <vacantcell> List = vacantcells; Final int COUNT = List. size (); For (INT I = 0; I <count; I ++) {list. get (I ). release ();} List. clear ();} Public String tostring () {return "cellinfo: [cellx =" + cellx + ", celly =" + celly + ", cellhspan =" + cellhspan + ", cellvspan = "+ cellvspan +"] ";}/*** vacantcell: represents an empty cell. It is composed of multiple cells and is implemented as a cell pool, reduce object creation **/static final class vacantcell {Private Static final int pool_size = 100; // The pool caches up to 100 vacantcellprivate static final object mlock = new object (); // used as the synchronization lock Private Static vacantcell mroot; Private Static int count; private vacantcell mnext; // vacantcell size information private int cellx; private int celly; private int cellhspan; private int cellvspan; public static vacantcell acquire () {synchronized (mlock) {If (mroot = NULL) {return New vacantcell (); // when there is no such thing at the beginning, if a pool exists, vacantcell info = mroot and mroot = info are obtained from the pool. mnext; count --; // remember to update return Info;} // The Public void release () {synchronized (mlock) {If (count <pool_size) of the release object) {count ++; mnext = mroot; mroot = This ;}}}}}}
It uses cellinfo to save the view information and its location information at the current location, but it is noted that it also defines a vancantcell class, which represents an empty "region ", this area may have multiple cells. At the same time, it is implemented as a cell pool of the linked list structure, so that you do not need to create new objects every time to optimize performance.
After learning about the approximate structure of celllayout, we can look for the answers to the three questions we mentioned at the beginning.
1. How to identify information at the current position
To know whether a position is empty or occupied, celllayout uses a two-dimensional Boolean array Boolean [number of horizontal cells] [number of vertical cells] To save the occupied information of each cell, the occupied value is true, and the null value is false.
To determine whether the position of the current long-press event is on the item, you can use the onintercepttouchevent method to determine whether the position of the current long-press event is in the position of an item. As follows:
Final rect frame = mrect; Final int x = (INT) eV. getx (); Final int y = (INT) eV. gety (); log. V (TAG, "motionevent. getx, Gety: [x, y] = ["+ x +", "+ Y +"] "); Final int COUNT = getchildcount (); Boolean found = false; log. V (TAG, "celllayout Child Count:" + count); For (INT I = count-1; I> = 0; I --) {final view child = getchildat (I); If (child. getvisibility () = visible | child. getanimation ()! = NULL) {child. gethitrect (FRAME); // obtains the child size information, relative to celllayoutlog. V (TAG, "view. gethitrect: "+ frame. tostring (); If (frame. bottom <= frame. top | frame. right <= frame. left) {log. V (TAG, "the rectangle of the view is incorrect"); continue;} If (frame. contains (x, y) {// if the current event falls on the child, final layoutparams Lp = (layoutparams) child. getlayoutparams (); cellinfo. view = Child; cellinfo. cellx = LP. cellx; cellinfo. celly = LP. Celly; cellinfo. cellhspan = LP. cellhspan; cellinfo. cellvspan = LP. cellvspan; cellinfo. Valid = true; found = true; log. V (TAG, "Yes, found! "); Break ;}}}
We have recorded the current location information and view information if it falls on an item. What if the current long press is an empty area?
If (! Found) {/*** if you click a blank area, you also need to save the current location information * When you click a blank area, you need to do more processing, in the outer pop-up dialog box, add applications, folders, shortcuts, etc. Then, create the icon */INT cellxy [] = mcellxy; pointtocellexact (X, Y, cellxy) at the * position on the desktop ); // obtain the log of the cell where the current event is located. V (TAG, "not found the cellxy is = [" + cellxy [0] + "," + cellxy [1] + "]"); // Save the current location information cellinfo. view = NULL; cellinfo. cellx = cellxy [0]; cellinfo. celly = cellxy [1]; cellinfo. cellhspan = 1; cellinfo. cellvspan = 1; // here you need to calculate which cells are occupied by final int x Count = mhcells; // todo: Final int ycount = mvcells; Final Boolean [] [] occupied = moccupied; findoccupiedcells (xcount, ycount, occupied) are not taken into account ); // determine whether the current position is valid. Here, you do not need to determine whether cellxy is out of bounds, because you have processed cellinfo in pointtocellexact. valid =! Occupied [cellxy [0] [cellxy [1]; // In fact, we need to center the cells represented by cellinfo, opening up continuous maximum space for recursion around the world // However, this is not required here, only when the gettag () method is called, it indicates that you need to put a view in a region. Therefore, put the opened method in gettag () and call // to mark mtagflag = true ;} // save location information in the tag of celllayout settag (cellinfo );
Long press a region and record the current location information. Note that we record the cell where the current event is located, and then save the information of the cell, the pointtocellexact method is called to calculate the cell in which the coordinate of the current event falls, and the findoccupiedcells method is called to calculate the number of occupied cells in the entire celllayout. It is not difficult to calculate the correspondence between event coordinates and cells, because we know the width and height of each cell and the coordinates of the current event, so a simple division can be calculated.
2. How to add the added item to celllayout
We know that to draw the layout method for each child in onlayout, let's look at the implementation of this method:
protected void onLayout(boolean changed, int l, int t, int r, int b) {int count = getChildCount();for(int i=0; i<count; i++){View child = getChildAt(i);if(child.getVisibility() != GONE){LayoutParams lp = (LayoutParams)child.getLayoutParams();child.layout(lp.x, lp.y, lp.x+lp.width, lp.y+lp.height);}}}
Note that the layout of each child in this method is based on the information stored in their own layoutparams object. Next, we will analyze the layoutparams structure of each child in celllayout. There is a custom layoutparams class in celllayout, which stores the information of the cell where the child is located and its actual coordinate position. It contains a set method, in which the child's width is calculated, height, starting coordinates X and Y.
Public void set (INT cellwidth, int cellheight, int hstartpadding, int vstartpadding, int widthgap, int heightgap) {// when calculating the width and height of an item, note that width and height are the attributes of margin. width = cellhspan * cellwidth + (cellHSpan-1) * widthgap-leftmargin-rightmargin; this. height = cellvspan * cellheight + (cellVSpan-1) * heightgap-topmargin-bottommargin; log. V (TAG, "the width and height of the view are:" + this. width + "," + this. height); // calculate the real coordinates of the item at the same time. // remove the margin and padding of the item, and the position of the View starting from this. X = cellx * (cellwidth + widthgap) + leftmargin + hstartpadding; this. y = celly * (cellheight + heightgap) + topmargin + vstartpadding; log. V (TAG, "the X and Y of the view are:" + this. X + "," + this. Y );}
At this time, you will know the width and height of each child, and how to convert the layout to its actual coordinates based on the information of the cell. We know that the layout of the control goes through two phases: one is measure and the other is layout. Measure mainly completes the control ing and calculates the space information required for each control to draw. Therefore, in onmeasure, You can naturally see that when you measure the size of each child, the set method is also called for it. As follows:
Int COUNT = getchildcount (); log. V (TAG, "onmeasure start... "); For (INT I = 0; I <count; I ++) {// view child = getchildat (I) is measured for each subcontrol ); layoutparams Lp = (layoutparams) child. getlayoutparams (); // here, we need to encapsulate the calculation results into layoutparams for celllayout to use when layout the child control. // here, the screen must be treated differently. // todo: LP. set (mcellwidth, mcellheight, mhstartpadding, mvstartpadding, mhcellgap, mvcellgap); // the width and height of the Child control are obtained and INT cwidth = LP is encoded using measurespec. width; int cheight = LP. height; int cwidthspec = measurespec. makemeasurespec (cwidth, measurespec. exactly); int cheightspec = measurespec. makemeasurespec (cheight, measurespec. exactly); child. measure (cwidthspec, cheightspec );}
At this point, we have finished introducing the child's painting work on celllayout. But we have not mentioned how to add the selected item to the desktop when we press the desktop for a long time. You have to go back to the onlongclick method of launcher. Let's take a look:
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 (); If (cellinfo. view = NULL) {// The description is a blank area // activityutils. alert (getapplication (), "blank area"); log. V (TAG, "onlongclick, cellinfo. valid: "+ cellinfo. valid); If (cellinfo. valid) {// if it is a valid region // activityutils. alert (getapplication (), "valid region"); addcellinfo = cellinfo; showpassworddialog (request_code_setup, null) ;}} else {mworkspace. startdrag (cellinfo);} return true;
We can see that there is cellinfo = (cellinfo) V in onlongclick. gettag (); in this way, we know that in the onintercepttouchevent method above, we put cellinfo into the tag to return cellinfo information in the gettag method of celllayout. With the cellinfo information, we can call the celllayout. addview method to add the selected control to the desktop. In the workspace class, call the addinscreen method to set its cell information, and then directly call the celllayout. addview method to add it to celllayout.
Public void addinscreen (view child, int screen, int cellx, int celly, int spanx, int spany, Boolean insertfirst) {If (screen <0 | screen> = getchildcount ()) {Throw new illegalargumentexception ("the screen must be >=0 and <" + getchildcount ();} final uordercelllayout group = getcelllayout (screen); uordercelllayout. layoutparams Lp = (uordercelllayout. layoutparams) child. getlayoutparams (); // initialize the view to be added in C In elllayout, the layout parameter if (Lp = NULL) {Lp = new uordercelllayout. layoutparams (cellx, celly, spanx, spany);} else {LP. cellx = cellx; LP. celly = celly; LP. cellhspan = spanx; LP. cellvspan = spany;} log. V (TAG, "Before add view on the screen"); group. addview (child, insertfirst? 0:-1, LP); Child. setonlongclicklistener (monlongclicklistener); // The onclicklistener of child is set when the view is created, in uorderlauncher}
We can see that the group is called directly. addview method, but previously we only set the cell information in the Child layoutparams. As for how to obtain the actual coordinates and layout based on the information, we have already introduced it.
So far, celllayout has been roughly introduced. But celllayout is not that simple. When we add more than one cell, we need to consider how to allocate its space and how to handle problems such as celllayout if there is not enough space. However, if I want to understand the above, this is not a problem...