In the middle of the round head and Halo waveform explanation please see: http://blog.csdn.net/cj_286/article/details/52839036
The surrounding bubble layout, because the layout ratiolayout is inherited from the ViewGroup, so layout layouts can be based on their own needs to layout its sub-view,view.layout (int l,int t,int r,int b); Used to position the child view in the parent viewgroup (relative to the parent container), so the left,top,right,bottom of all child view is computed in ratiolayout. So the bubble view around the avatar is how to calculate its left,top,right,bottom, these bubbles view is located in the outer circle of the head, just know the radius of the ring, and then according to the number of bubbles, calculate the angle between each bubble, The RADIUS plus angle calculates where each bubble sits.
/** * Calculate the layout position of the bubbles * @param textviews */private void Calculateratioframe (list<bubbleview> textviews) { if (textviews.size () = = 0) return; Mratioframelist.clear (); Double angle = 0;//records the angle of each bubble, just above the 0°double grad = Math.PI * 2/textviews.size ();//gradient, the angle between each textview (Math.PI is a mathematical In 90°) Double Rightangle = math.pi/2;//Lap is 360°, altogether four directions, each direction is 90°, we calculate according to less than equals 90 °, then put in the corresponding direction//cx,cy is the center point of the container, also is the center point of the Circle head , the position of the calculated Bubbles is the Cx,cy calculated as the datum of the int cx = mwidth/2;//Container Center x coordinate int cy = mheight/2;//Container Center y coordinate int radius = MMi NSIZE/2/2/2 + MMINSIZE/2/2;//dynamic bubble composition Circle radius int left = 0; int top = 0; int right = 0; int bottom = 0; int a = 0,b = 0;//a is a CX-based offset, B is an offset based on CY,//int r = MMINSIZE/6/2;//bubble radius for (int i = 0; i < textviews. Size (); i++) {int r = textviews.get (i). Getmeasuredwidth ()/2;//calculated//fixed dead MMINSIZE/6/2;//bubble radius if (angle >= 0 && Angle < RightaNgle) {//0-90 degrees is the calculated offset//Hold angle at 0-90 a = (int) (RADIUS * Math.sin (math.abs (angle% Rightang Le)); b = (int) (RADIUS * Math.Cos (math.abs (angle% rightangle)); left = cx + A-R;//CX + A is the center point of the bubble, and you need to subtract the radius r top = cy-b-R If you want to go. right = left + 2 * r; Bottom = top + 2 * r; }else if (angle >= rightangle && Angle < Rightangle * 2) {//90-180 a = (int) (RADIUS * Math). Sin (math.abs (angle% rightangle)); b = (int) (RADIUS * Math.Cos (math.abs (angle% rightangle)); left = cx + b-r; top = cy + a-r; right = left + 2 * r; Bottom = top + 2 * r; }else if (angle >= rightangle * 2 && Angle < Rightangle * 3) {//180-270 a = (int) (RADIUS * Math.sin (math.abs (angle% rightangle)); b = (int) (RADIUS * Math.Cos (math.abs (angle% righ(tangle))); left = cx-a-R; top = cy + b-r; right = left + 2 * r; Bottom = top + 2 * r; }else if (angle >= rightangle * 3 && Angle < Rightangle * 4) {//270-360 a = (int) (RADIUS * M Ath.sin (math.abs (angle% rightangle)); b = (int) (RADIUS * Math.Cos (math.abs (angle% rightangle)); left = Cx-b-R; top = cy-a-R; right = left + 2 * r; Bottom = top + 2 * r; }//Will calculate the left, top, Right,bottom,angle saved up Mratioframelist.add (new Ratioframe (left, top, Right,bottom,angle )); Angle plus a gradient value angle + = Grad; } }
Calculate the layout of the bubbles left, top, Right,bottom, start the layout of the bubble, the layout of the code is much simpler
@Override protected void OnLayout (Boolean changed, int l, int t, int r, int b) {if (Mimageview = = null) return; int width = mimageview.getmeasuredwidth ();//calculate the width int height of the circle head = Mimageview.getmeasuredheight ();//Calculate the height of the round head/ /Calculate the left, top of the circle head, Right,bottom int left = MWIDTH/2-WIDTH/2; int top = MHEIGHT/2-HEIGHT/2; int right = MWIDTH/2 + WIDTH/2; int bottom = MHEIGHT/2 + height/2;//start layout mimageview.layout (Left,top,right,bottom);//Layout love animation for (int i = 0; I < mlovexinlist.size (); i++) {ImageView ImageView = Mlovexinlist.get (i); left = Mwidth/2 + WIDTH/4-imageview.getmeasuredwidth ()/2; Bottom = Mheight/2 + HEIGHT/3; top = Bottom-imageview.getmeasuredheight (); right = left + imageview.getmeasuredwidth (); Imageview.layout (Left,top,right,bottom); }//Layout all bubbles for (int i = 0; i < mtextviews.size (); i++) {TeXtview TextView = Mtextviews.get (i); Ratioframe ratioframe = Mratioframelist.get (i);//animation when used//animated, left, top, right,bottom are changed if (Mcurre Ntratioframelist! = null) {//valueanimator Execution animation is the result of all bubbles left, top, right,bottom ratioframe ratioframe = mcur Rentratioframelist.get (i); Textview.layout (Ratioframe.mleft,ratioframe.mtop,ratioframe.mright,ratioframe.mbottom); } } }
OK, the static bubble layout here is good, the following question is how to expand the bubble from the center point, the curved path to expand, and the size of the bubbles from small to large changes. Here is the use of the animation class Valueanimator and Scaleanimation, detailed please refer to: http://blog.csdn.net/cj_286/article/details/53020725
Outward-expanding effect we can use View.layout () to constantly re-layout bubble view, let it produce a translation effect, the following question is how to calculate the translation track above the left, top, Right,bottom, and then re-request layout can be, So here's how to figure out how to calculate this trajectory, analyze
Arc trajectory calculation, in fact, is in the line trajectory based on the offset (Movex and Movey), the formation of an arc trajectory, linear trajectory is very good calculation, the key is this offset, because in the first offset is small, and in the middle of the offset is large, and in different directions, The value of Movex and Movey is not the same as the positive or negative. The offset distance is from small to large and from large to small, so we use the two-time function (-2 * MATH.POW (fraction,2) + 2 * fraction) to calculate the distance, multiply the value of this two function by a set maximum value, This maximum value will be changed from small to large, then to small, and then to a different angle to calculate its positive and negative
if (endratioframe.mangle >0 && endratioframe.mangle <= rightangle) {//(0 < angle <= 90) Move up, move left MoveX = (int) (temp * Math.Abs (Math.Cos (Endratioframe.mangle)));//Move up should subtract moveX Movey = (int) on the original trajectory (temp * Math.Abs (Math.sin (Endratioframe.mangle))); }else if (Endratioframe.mangle > Rightangle && endratioframe.mangle <= rightangle * 2) {//(All < angle <= 180) Move right, move up MoveX = (int) (-TEMP * Math.Abs (Math.Cos (Endratioframe.mangle))); Movey = (int) (temp * Math.Abs (Math.sin (Endratioframe.mangle))); }else if (Endratioframe.mangle > Rightangle * 2 && endratioframe.mangle <= Rightangle * 3) {//(+ + < angle <= 2700) Move down, move right MoveX = (int) (-TEMP * Math.Abs (Math.Cos (Endratioframe.mangle))); Movey = (int) (-TEMP * Math.Abs (Math.sin (Endratioframe.mangle))); }else if (Endratioframe.mangle > Rightangle * 3 && endratioframe.mangle < = Rightangle * 4 | | Endratioframe.mangle = = 0) {//(< angle <= 360 or angle = 0) Move left, move down MoveX = (int) (TEMP * Math.Abs ( Math.Cos (Endratioframe.mangle)); Movey = (int) (-TEMP * Math.Abs (Math.sin (Endratioframe.mangle))); }
Depending on the value of the trigonometric function, the above code can be simplified to
MoveX = (int) (temp * Math.Cos (endratioframe.mangle)); movey = (int) (temp * Math.sin (endratioframe.mangle));
By using the above calculation formula logic, we can get the implementation class of the type estimator when the bubble expands, and the exit bubble will reverse the logic.
Package Com.cj.dynamicavatarview.ratio;import Android.animation.typeevaluator;import Android.content.Context; Import Android.util.typedvalue;import java.util.arraylist;import java.util.list;/** * Created by Chenj on 2016/10/19. */public class Enterratioframeevaluator implements Typeevaluator {public static final int offset_distance = 80; Private Context Mcontext; private int moffsetdistance; Public Enterratioframeevaluator (Context context) {This.mcontext = context; moffsetdistance = (int) typedvalue.applydimension (typedvalue.complex_unit_dip,offset_distance, Mcontext.getresources (). Getdisplaymetrics ()); } @Override Public Object evaluate (float fraction, object Startvalue, Object Endvalue) {LIST<RATIOFRAME&G T Startratioframelist = (list<ratioframe>) startvalue;//start value list<ratioframe> endRatioFrameList = (List< ; ratioframe>) endvalue;//End value list<ratioframe> ratioframelist = new arraylist<> ();//The resulting value for (int i = 0; i < endratioframelist.size (); i++) {Ratioframe endratioframe = Endratioframelist.get (i); Ratioframe startratioframe = Startratioframelist.get (i); Calculation Left,top,right,bottom Double t = (-2 * MATH.POW (fraction,2) + 2 * fraction);//tilt change rate int temp = (int) ((moffsetdistance) * t); Double rightangle = MATH.PI/2; int MoveX = 0,movey = 0; Move the bubble up, down, left, and right to form a translation path of radians MoveX = (int) (temp * Math.Cos (endratioframe.mangle)); Movey = (int) (temp * Math.sin (endratioframe.mangle)); Get left again, Top,right,bottom int left = (int) (Startratioframe.mleft + (ENDRATIOFRAME.MLEFT-STARTRATIOFRAME.M left) * fraction)-MoveX); int top = (int) (Startratioframe.mtop + ((endratioframe.mtop-startratioframe.mtop) * fraction)-Movey); int right = (int) (Startratioframe.mright + ((endratioframe.mright-startratioframe.mright) * fraction)-MoveX); int bottom = (int) (Startratioframe.mbottom + ((endratioframe.mbottom-startratioframe.mbottom) * fraction)-MoveY ) ; Ratioframelist.add (New Ratioframe (Left,top,right,bottom)); } return ratioframelist; }}
The valueanimator can be used to achieve a curved translational trajectory.
Valueanimator manimatorenetr = valueanimator.ofobject (New Enterratioframeevaluator (GetContext ()), Getratioframecenterlist (mratioframecenter,mratioframelist), mratioframelist); Manimatorenetr.addupdatelistener (New Valueanimator.animatorupdatelistener () { @Override public Void Onanimationupdate (Valueanimator animation) { //Get new layout value mcurrentratioframelist = (list<ratioframe>) Animation.getanimatedvalue (); Request re -layout requestlayout (); } ); Manimatorenetr.setduration (open_bubble_time); Manimatorenetr.start ();
Well, the Arc animation from the center point outward is realized, and then with the scaled animation, the scaled animation can be implemented using the view animation.
/** * Bubbles from small to large scale * @param textviews * /private void Scalesmalltolarge (list<bubbleview> textviews { //With the View Center as the zoom point, from the initial state to the scaleanimation animation = new Scaleanimation ( 0.0f, 1.0f,//a little bit smaller until you see the invisible. 0.0f, 1.0f, Animation.relative_to_self, 0.5f, animation.relative_to_self, 0.5f//Intermediate scale ); Animation.setduration (open_bubble_time);//The time for the peace to move is consistent for (int i = 0; i < textviews.size (); i++) { //re-execute animation C13/>textviews.get (i). Startanimation (animation); } }
The following is to solve the expansion, the bubble began to float, click on the bubble to stop floating, after sliding the finger after the bubble followed the finger movement, release the bubble back to the original position, the return of the animation effect and bubble expanded animation is very similar, the bubbles follow the finger movement is also very good to achieve, Just set the bubble view to the Ontouch event, then ontouch the distance to calculate the slide, and then re-view.layout () on it, so here we can solve the floating problem. The float is irregular, and the distance and speed of the float is not the same, I use the view animation is not very good, and then use the property animation to achieve. Just shift the view to the X and Y axes, so that the distance and time are different, and it looks like an irregular movement, so that it can be shifted over and over again to achieve a floating effect.
/** * Set a floating effect for the specified view * @param view * @return */private Animatorset setanimfloat (view view) {Li st<animator> animators = new arraylist<> (); GETRANDOMDP () gets a random value objectanimator Translationxanim = objectanimator.offloat (view, "Translationx", 0f,getRandom Dp (), GETRANDOMDP (), 0); Translationxanim.setduration (Getrandomtime ()); Translationxanim.setrepeatcount (valueanimator.infinite);//infinite Loop Translationxanim.setrepeatmode ( Valueanimator.infinite);//Translationxanim.setinterpolator (New Linearinterpolator ()); Translationxanim.start (); Animators.add (Translationxanim); Objectanimator Translationyanim = objectanimator.offloat (view, "Translationy", 0F,GETRANDOMDP (), GetRandomDp (), 0); Translationyanim.setduration (Getrandomtime ()); Translationyanim.setrepeatcount (Valueanimator.infinite); Translationyanim.setrepeatmode (Valueanimator.infinite); Translationxanim.sEtinterpolator (New Linearinterpolator ()); Translationyanim.start (); Animators.add (Translationyanim); Animatorset animatorset = new Animatorset (); Animatorset.playtogether (animators); Animatorset.setstartdelay (delay); Animatorset.start (); return animatorset; }
Press and hold the stop floating, release the time to return to the position, and then again floating, if the Animator.end () method, after the beginning of the float will appear flashing phenomenon, because the property animation, although you can change the location of the view, but will not change the view of the Left,top,right, Bottom, so there will be flashing when the float starts again, because x = Mleft + Translationx, when the restart, the property animation is recreated, the Translationx is starting from 0, so there will be a flicker of the phenomenon.
Final Animatorset Animatorset = manimatorsetlist.get (position); For (Animator animator:animatorSet.getChildAnimations ()) { //execute to the animation finally, revert to the initial position, or start floating again, there will be a flashing bug if ( Animator.isrunning ()) { animator.end ();//execute to animation last animator.cancel ();//Cancel Animation } }
The process is almost there, but when the bubbles move to the inside of the circle head release, the bubble should have a scaling effect after the return, and then there should be an interface callback, tell the caller, I went to the middle of the release, you can do some corresponding processing. Now we look at how to calculate the bubble has moved to the Avatar, in fact, through the center point of the Circle Head and the center of the bubble form a direct triangle, and then through the Pythagorean theorem, calculate the length of the right angle and the radius of the circle head to do a comparison, if less than the radius of the circle head, it is already into the head
/** * To determine if the center point of the bubble is inside the picture * @param view * @param current position to which you are currently moving * @param endratioframe If in the middle, this value is used to reset to the original position * @return */private Boolean isinpicturecenter (int position,view view,ratioframe current,ratioframe endratioframe {Ratiopoint CenterPoint = new Ratiopoint (MWIDTH/2,MHEIGHT/2); Ratiopoint currentpoint = new Ratiopoint (Current.mleft + ((current.mright-current.mleft)/2), Current.mtop + ((current.m Bottom-current.mtop)/2)); int x = Math.Abs (centerpoint.x-currentpoint.x); int y = Math.Abs (CENTERPOINT.Y-CURRENTPOINT.Y); Calculate the distance between two points by the Pythagorean theorem int edge = (int) math.sqrt (Math.pow (x,2) + Math.pow (y,2)); int pictureradius = Mimageview.getpictureradius (); Then compare the half catty with the internal picture, less than Pictureradius, on the inside if (Pictureradius > Edge) {//Enter into the internal if (minnercenterlistener! = nul L) {minnercenterlistener.innercenter (position, ((TextView) view). GetText (). toString ()); }//Description to the center,Row bubble Scaling Revesescaleview (position, view,current,endratioframe); return true; } return false; }
Bubble Execution Scaling
/** * Zoom picture (motion Tween) * @param view * @param The starting point for panning after current zoom * @param endratioframe for panning after zoom */PU Blic void Revesescaleview (final int position, Final view view, Final Ratioframe current, final Object endratioframe) { Zoom point in the View center, reduced from initial state to scaleanimation animation = new Scaleanimation (1.0f, 0.0f,//a little bit smaller. Not yet 1.0f, 0.0f, Animation.relative_to_self, 0.5f, animation.relative_to_self, 0.5f//Intermediate zoom ); Animation.setduration (Bubble_enter_center_scale_time); Animation.setrepeatmode (Animation.reverse); Animation.setrepeatcount (1); Animation.setanimationlistener (New Animation.animationlistener () {@Override public void onanimation Start (Animation Animation) {} @Override public void Onanimationend (Animation Animation) {//After performing the zoom, let the bubble return to the bit, after the end of the return, execute the interface callback Homingbubbleview (True,position,view, current, endratioframe); } @Override public void Onanimationrepeat (Animation Animation) {}}); View.startanimation (animation); }
Interface callback definition for bubble entry Center
public interface innercenterlistener{ //Enter center, release return to call void innercenterhominged (int position, String text); Enter center, release call void innercenter (int position, String text); }
The following is the execution plus 1 operation and play Love animation, these two animation is the execution of two view animation, here is not posted out, to here high imitation QQ personality card on the end of the explanation, if not good or have questions welcome message
SOURCE Download: GitHub
CSDN
High-imitation QQ personality card