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
Originally, this article should still write the implementation principle of the item drag in launcher. However, since studying launcher, it has not been implemented before, and now it is all inspired. This is not the case. A month ago, I saw the well-known accounting software casually. I saw a cool statistical pie chart in Android. At that time, I downloaded the APK and decompiled it, I don't know if it has been processed in the code. I didn't decompile the source code, and I didn't know it at all. Only resource files are decompiled successfully. At that time, this incident was put down, although unwilling. However, there is no such thing on the Internet.
But after studying the launcher, I thought about its implementation principle and suddenly realized that today, the same function was finally realized. Now I think that after analyzing the launcher source code, I have a lot of new ideas about custom controls.
If you don't talk nonsense, let's take a look at the notes:
How is it so dazzling? In addition, the finger can rotate the pie chart at will. When I saw it, my eyes were bright and I really wanted to implement it myself. Now let's take a look at the effect of the Implementation. Although the same function is implemented, the color is not as bright as it is.
Only one image can be seen after decompilation:
In fact, you don't underestimate this picture. I really admire the maker of this picture. If my PS technology is half superb, I can eat at PS. Ah, so I used it first. The code is directly pasted below, and the comments in the specific code are clear.
/***** Implementation principle of the cool pie chart that can be rotated as needed ** Note: * in the implementation process, it mainly used some mathematical calculations to calculate the angle and the coordinates of the screen position * concerning the angle calculation between any two points, I thought for a long time that there was no result. Finally, an accidental aura makes the entire * thing easy, that is, when calculating the angle between any two points and the Center Coordinate, first, calculate * The angle of each point relative to the positive direction of the X axis, in this way, we can always convert it to the problem of calculating the inner angle of the right triangle * And then perform the subtraction operation on the two calculation angles. Is it easy? Haha, for developers who have not learned well in mathematics like ours, it is also difficult to simplify this process. ** @ author liner **/public class piechart extends view {public static final string tag = "piechart"; public static final int alpha = 100; public static final int animation_duration = 800; public static final int animation_state_running = 1; public static final int animation_state_down = 2;/*** do not ask me how to set this value. This is the Coordinate Position of the four sides of the rectangle corresponding to a large circular area in the image * specific value. You have tried and adjusted it multiple times. In this way, the pie chart is relative to the */Private Static final rectf oval = new rectf (303,340,) drawn in this region; private int [] colors; // The color value of each part is private int [] values; // the size of each part is private int [] degrees; // The value is converted to the angle private string [] titles; // The content of each part is private paint; private paint maskpaint; private paint textpaint; private point lasteventpoint; private int currenttargetindex =-1; private point center; // This is the center position of the pie chart. Private int eventradius = 0; // The distance between the event and the pie chart center // The/private chartclicklistener clicklistener and private bitmap mask used for testing; // The bitmapprivate int startdegree = 90 used for the mask; // At the beginning, the pie is the private int animstate = animation_state_down drawn from the arrow position; private Boolean animenabled = false; private long animstarttime; Public piechart (context) {super (context); Init ();} public piechart (context, attributeset attrs) {This (context, attrs, 0);} pub LIC piechart (context, attributeset attrs, int defstyle) {super (context, attrs, defstyle); Init () ;}private void Init () {paint = new paint (); maskpaint = new paint (); textpaint. setcolor (color. white); textpaint. settypeface (typeface. default_bold); textpaint. setalpha (100); textpaint. settextsize (16); values = new int [] {60, 90, 30, 50, 70}; // titles = new string [] {// "Sichuan", // "Hui Cai ", // "Cantonese cuisine ",/ /"Min Cai", // "Xiang Cai" //}; // titles = new string [] {"I'm three years old" in the center of the test text, "I have no choice but to say ", "Shi da", "Steven sendi", "Xiang"}; colors = new int [] {color. argb (alpha, 249, 64, 64), color. argb (alpha, 0,255, 0), color. argb (alpha, 255, 0,255), color. argb (alpha, 255,255, 0), color. argb (alpha, 0,255,255)}; degrees = getdegrees (); // drawable d = getresources (). getdrawable (R. drawable. mask); mask = bitmapfactory. decoderesource (getresources (), R. drawable. Mask); // when obtaining the initial position, animenabled = true in the area where the arrow below is located; // meanwhile, start the animation} // public void setchartclicklistener (chartclicklistener L) {// This. clicklistener = L; // calculates the sum of private int sum (INT [] values) {int sum = 0; For (INT I = 0; I <values. length; I ++) {sum + = values [I];} return sum;}/*** according to the proportion of each part, to calculate the angle occupied by each area in the entire circle * However, there is a small detail, that is, when calculating, note that it may not be divisible. At this time, to * avoid all angles and conditions less than 360 degrees, just give the remaining parts to a certain part without affecting * @ return */private int [] G. Etdegrees () {int sum = This. sum (values); int [] degrees = new int [values. length]; for (INT I = 0; I <values. length; I ++) {degrees [I] = (INT) math. floor (double) Values [I]/(double) sum) * 360); // log. V ("angle", angles [I] + "");} int anglesum = This. sum (degrees); If (anglesum! = 360) {// The calculation above may cause and less than 360int c = 360-anglesum; degrees [values. length-1] + = C; // Let the last value be slightly larger} return degrees ;} /*** override this method to draw the entire interface */protected void ondraw (canvas) {super. ondraw (canvas); If (animenabled) {/*** indicates that a pie chart */log needs to be rotated during startup. E (TAG, "anim enabled"); If (animstate = animation_state_down) {animstarttime = systemclock. uptimemillis (); animstate = animation_state_running;} final long Cu R1_timediff = systemclock. uptimemillis ()-animstarttime; int currentmaxdegree = (INT) (float) currenttimediff/animation_duration * 360f); log. E (TAG, "the current maximum degree is:" + currentmaxdegree); If (currentmaxdegree> = 360) {// animation end state, stop painting currentmaxdegree = 360; animstate = animation_state_down; animenabled = false;} int [] degrees = getdegrees (); int startangle = This. startdegree; // obtain the region where the maximum rotation angle is currently available. Int maxindex = G Eteventpart (currentmaxdegree); // pie chart based on different colors for (INT I = 0; I <= maxindex; I ++) {int currentdegree = degrees [I]; if (I = maxindex) {// for the current last drawing area, it may be only a part. You need to obtain its offset currentdegree = getoffsetofpartstart (currentmaxdegree, maxindex );} if (I> 0) {// note that every pie chart is painted, remember to calculate startangle + = degrees [I-1];} paint. setcolor (colors [I]); canvas. drawarc (oval, startangle, currentdegree, true, paint);} If (animstate = Animation_state_down) {// If the animation ends, adjust the onstop () ;}else {postinvalidate ();}} else {int [] degrees = getdegrees (); int startangle = This. startdegree;/*** the color of each region is different, but here you only need to control the angle of each region. The whole is a circle */For (INT I = 0; I <values. length; I ++) {paint. setcolor (colors [I]); if (I> 0) {startangle + = degrees [I-1];} canvas. drawarc (oval, startangle, degrees [I], true, paint);}/*** draw a pie chart and mask the image. The figure above forms the mask effect */canvas. drawbitmap (mask, 0, 0, maskpaint);/*** the information displayed in the region where the calculated arrow is located */If (currenttargetindex> = 0) {String title = titles [currenttargetindex]; textpaint. setcolor (colors [currenttargetindex]); // perform a simple calculation to display int width = title in the center of the text. length () * 17; canvas. drawtext (title, 157-width/2 + 3,383, textpaint) ;}/ *** process the rotation of the pie chart */Public Boolean ontouchevent (motionevent event) {If (animenabled & Amp; animstate = animation_state_running) {return Super. ontouchevent (event);} Point eventpoint = geteventabsolutelocation (event); computecenter (); // calculate the Center Coordinate // calculate the angle of the current position relative to the positive direction of the X axis // calculate the int newangle = geteventangle (eventpoint, center) of eventradius in the following method ); int action = event. getaction (); Switch (Action) {Case motionevent. action_down: lasteventpoint = eventpoint; If (eventradius> getradius () {/*** only points in The pie chart must be rotated internally. Otherwise, the system returns */log directly. E (TAG, "the current location exceeds the radius:" + eventradius + ">" + getradius (); return Super. ontouchevent (event);} break; Case motionevent. action_move: // handle slide rotate (eventpoint, newangle) here; // after processing, remember to update lasteventpointlasteventpoint = eventpoint; break; Case motionevent. action_up: onstop (); break; default: break;} return true;}/*** when we stop rotating, if the arrow at the bottom of the current area is not central, then we need to calculate the * offset and point the arrow to the center position */private void onstop () {Int targetangle = gettargetdegree (); currenttargetindex = geteventpart (targetangle); int offset = getoffsetofpartcenter (targetangle, currenttargetindex);/*** Offset> 0, it indicates that the current arrow is on the right of the center position, and the offset angle * offset <0 for all regions is rotated clockwise, which is the opposite */startdegree + = offset; postinvalidatedelayed (200 );} private void rotate (point eventpoint, int newdegree) {// calculate the angle of the last position relative to the positive direction of the X axis int lastdegree = geteventangle (lasteventpoint, c Enter);/*** in fact, rotation is the starting angle when the arc is constantly updated. In this way, each time the arc is re-drawn from the new starting angle, the rotation effect is formed */startdegree + = newdegree-lastdegree; // when multiple turns are performed, startangle must always be between-360-360 degrees if (startdegree >=360) {startdegree-= 360;} else if (startdegree <=-360) {startdegree + = 360;} log. E (TAG, "Current startangle:" + startdegree); // obtain the region where the current arrow is located, in this way, the information int targetdegree = gettargetdegree (); currenttargetindex = geteventpart (targ Etdegree); // request to re-draw the interface and call the ondraw method postinvalidate ();} /*** get the coordinates of the current event relative to the screen * @ Param event * @ return */protected point geteventabsolutelocation (motionevent event) {int [] Location = new int [2]; this. getlocationonscreen (location); // the position of the current control on the screen int x = (INT) event. getx (); int y = (INT) event. gety (); x + = Location [0]; y + = Location [1]; // X, Y indicates the coordinates of the current event relative to the screen. point P = new point (x, y); log. V (TAG ," Event coordinates: "+ P. tostring (); Return P;}/*** get the center coordinate of the current pie chart, relative to the top left corner of the screen */protected void computecenter () {If (center = NULL) {int x = (INT) Oval. left + (INT) (oval. right-OVAL.left)/2f); int y = (INT) Oval. top + (INT) (oval. bottom-oval. top)/2f) + 50; // The height of the status bar is 50 Center = new point (x, y); // log. V (TAG, "Center Coordinate:" + center. tostring () ;}}/*** obtain the radius */protected int getradius () {int radius = (INT) (oval. right-OVAL.left)/2f); // Lo G. V (TAG, "radius:" + radius); Return radius;}/*** get the angle of event coordinates to the square direction of the center X axis of the pie chart * here is the Coordinate System Conversion, in this example, the center of the pie chart is used as the coordinate center, which is the "normal" coordinate system that we have been using from junior high school to university. * But the circle rotation is involved, in this example, the position of an event in the coordinate system is calculated clockwise relative to the positive direction of x * @ Param eventpoint * @ Param center * @ return */protected int geteventangle (point eventpoint, Point center) {int x = eventpoint. x-center. x; // offset of the X axis direction int y = eventpoint. y-center. y; // y-axis offset // log. V (TAG, "Cartesian triangle two straight side length:" + x + "," + Y); Double Z = math. hypot (math. ABS (x), math. ABS (y); // obtain the length of the oblique side of the right triangle // log. V (TAG, "oblique edge length:" + Z); eventradius = (INT) Z; double Sina = (double) math. ABS (y)/Z; // log. V (TAG, "Sina =" + Sina); double Asin = math. asin (SINA); // obtain the angle of the current vertex and the X axis, which is the smallest. // log. V (TAG, "arc sine of the current relative offset angle:" + asin); int degree = (INT) (asin/3.14f * 180f); // log. V (TAG, "current relative offset angle:" + angle); // The following must be based on the positive and negative values of X and Y, int realdegree = 0; If (x <= 0 & Y <= 0) {// top left corner of the X axis, returns 180 + anglerealdegree = 180 + degree;} else if (x >=0 & Y <= 0) {// in the upper right corner, returns 360-anglerealdegree = 360-degree ;} else if (x <= 0 & Y> = 0) {// at the bottom left, return 180-anglerealdegree = 180-degree;} else {// At the bottom right and return realdegree = degree ;} // log. V (TAG, "the clockwise offset angle of the current event relative to the x-axis square of the central coordinate is:" + realangle); Return realdegree ;} /*** obtain the angle value of the current arrow position relative to startdegree. * Note that the downward arrow is 90 degrees opposite to the positive direction of the X axis. * @ return */protected int gettargetdegree () {int targetdegree =-1; int tmpstart = startdegree;/*** if the current startangle is negative, it is directly + 360 and converted to a positive value */If (tmpstart <0) {tmpstart + = 360;} If (tmpstart <90) {/*** if startangle is smaller than 90 degrees (may be negative) */targetdegree = 90-tmpstart ;} else {/*** if startangle is greater than 90, the maximum value of startangle is 360 degrees, therefore, * You can directly calculate according to the following formula */targetdegree = 360 + 90-tmpstart;} // log. E (TAG, "taget angle:" + targetdegree + "startangle:" + startangle); Return targetdegree ;} /*** determine which part of the pie chart the degree angle is located in. * Note that the angle here must be positive, and it is not relative to the positive direction of the X axis, instead, the Index * @ Param degree * @ return */protected int geteventpart (INT degree) {int currentsum = 0; For (INT I = 0; I <degrees. length; I ++) {currentsum + = degrees [I]; If (currentsum> = degree) {return I ;}} return-1 ;} /*** when you know that the current degree is in the targetindex area, calculates the offset * @ Param degree * @ Param targetindex * @ return */protected int getoffsetofpartstart (INT degree, int targetindex) of the angle relative to the start position of the region targetindex {int currentsum = 0; for (INT I = 0; I <targetindex; I ++) {currentsum + = degrees [I];} int offset = degree-currentsum; return offset ;} /*** when we know that the current degree is in the targetindex area, calculate the offset of angle to the center position of the region targetindex * this is when we stop rotating, by calculating the offset, to point the arrow to the center of the current region * @ Param degree * @ Param targetindex * @ return */protected int getoffsetofpartcenter (INT degree, int targetindex) {int currentsum = 0; for (INT I = 0; I <= targetindex; I ++) {currentsum + = degrees [I];} int offset = degree-(currentsum-degrees [targetindex]/2); // if the value is greater than half, Offset> 0; if the value is less than half, offset <0 return offset ;}}
Point Object used in the program:
public class Point {public int x;public int y;public Point(int x, int y){this.x = x;this.y = y;}public int[] getPoint(){int[] point = new int[2];point[0] = x;point[1] = y;return point;}public String toString(){return new StringBuilder("[").append(x).append(",").append(y).append("]").toString();}}