These two days learned to use path to draw Bezier curve related, and then do-it-yourself a similar QQ unread message can drag small bubbles, the effect is as follows:
Final effect Diagram
The next step is to implement the entire process.
Basic principle
In fact, using path to draw three points of the two-time Bezier curve to complete the enchanting curve. Then the corresponding circle is drawn according to the touch point constantly, and the radius of the original fixed circle is changed according to the distance. Finally, the implementation of the return or burst after letting go.
Path Introduction:
As the name suggests, is a path meaning, path there are many methods, the design of the main use of the relevant methods have
- MoveTo () Move path to a specified point
- Quadto () draws two Bezier curves, receives two points, the first is the point that controls radians, and the second is the endpoint.
- LineTo () is the connection.
- Close () Closes the path path,
- Reset () Related settings for reset path
Path Starter Warm-up:
Path.reset ();
Path.moveto (in);
The first coordinates are the corresponding control coordinates, and the second coordinates are the endpoint coordinates
(path.quadto).
Canvas.drawpath (path, paint);
Canvas.translate (0);
When Close is invoked, the connection
Path.close () is closed.
Canvas.drawpath (path, paint);
Remember not to new path or paint in the OnDraw Method!
Path
To implement a split:
The whole process is to draw a closed path path of two Bezier two curves, and then add two circles to it.
The closed path path implementation from the left point to draw two times the Bezier curve to the left lower point, the lower left line to the lower right, the lower right point two times the Bezier curve to the right, the last closed!!
Determination of relative coordinates
This is one of the difficulties inside this time, because it involves a sin,cos,tan in mathematics and so on, I actually forgot, and then the brain made up a bit, nonsense not much to say,
Why do you want to paint it yourself, because you know, in the 360 rotation process, the angle system is two sets of, if the use of a set to draw, the process of the current rotation of the curve overlap in the situation!
The problem has been thrown out, and then look directly at the code implementation!
Angle determination
According to the schematic diagram can be known, we can use the starting center coordinates and drag the center coordinates, according to the inverse tangent function to get the specific radian.
int dy = Math.Abs (circley-starty);
int dx = Math.Abs (CIRCLEX-STARTX);
Angle = Math.atan (dy * 1.0/dx);
OK, the startx,y here is the coordinates of the moving process. Angle is the corresponding radian (angle) that is obtained.
Related path drawing
Already mentioned in the process of rotation there are two sets of coordinate system, at first I was also very entangled in this coordinate system to determine, and then suddenly dawned, in fact, is equivalent to 13 quadrant positive proportion of growth, 24 quadrant, the inverse proportion of growth.
Flag = (Starty-circley) * (Startx-circlex) <= 0;
Add a flag to determine which coordinate system to use.
The most important thing is to draw the relevant path path!
Path.reset (); if (flag) {//First point Path.moveto ((float) (Circlex-math.sin (angle) * origin_radio), (float) (Circley-math.cos (angle) *
Origin_radio)); Path.quadto ((float) (startx + circlex) * 0.5), (float) (starty + Circley) * 0.5), (float) (Startx-math.sin (angle) * DR
Ag_radio), (float) (Starty-math.cos (angle) * drag_radio));
Path.lineto ((float) (startx + math.sin (angle) * drag_radio), (float) (Starty + math.cos (angle) * drag_radio)); Path.quadto ((float) (startx + circlex) * 0.5), (float) (starty + Circley) * 0.5), (float) (Circlex + math.sin (angle) * O
Rigin_radio), (float) (Circley + math.cos (angle) * origin_radio));
Path.close ();
Canvas.drawpath (path, paint); else {//First point Path.moveto ((float) (Circlex-math.sin (angle) * origin_radio), (float) (Circley + math.cos (angle) * O
Rigin_radio)); Path.quadto ((float) (startx + circlex) * 0.5), (float) (starty + Circley) * 0.5), (float) (Startx-math.sin (angle) * DR Ag_radio), (float) (Starty + math.cos (angle) * drag_RADIO));
Path.lineto ((float) (startx + math.sin (angle) * drag_radio), (float) (Starty-math.cos (angle) * drag_radio)); Path.quadto ((float) (startx + circlex) * 0.5), (float) (starty + Circley) * 0.5), (float) (Circlex + math.sin (angle) * O
Rigin_radio), (float) (Circley-math.cos (angle) * origin_radio));
Path.close ();
Canvas.drawpath (path, paint);
}
The code here is to Java the mathematical formula associated with the picture!
Here, in fact, the main work on the completion of almost!
Next, set the effect of paint to fill, and then draw two more circles
Paint.setstyle (Paint.Style.FILL)
canvas.drawcircle (Circlex, Circley, Origin_radio, paint);/default
Canvas.drawcircle (startx = 0?) Circlex:startx, Starty = 0? Circley:starty, Drag_radio, paint)/dragging
You can draw the desired effect!
Here we have to say Ontouch's deal!
Case motionevent.action_down://have an incident first intercept say!!
getparent (). Requestdisallowintercepttouchevent (true);
CurrentState = State_idle;
Animsetxy.cancel ();
STARTX = (int) ev.getx ();
Starty = (int) ev.getrawy ();
Break
Handle the pit of the event distribution!
Measurement and layout
This is basically passable, but our layout has not yet dealt with, math_parent is absolutely impossible to use to specific projects!
Measurement, if the discovery is not a precise mode, then manually calculate the required width and height.
@Override
protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {
int modewidth = Measurespec.getmode (WIDTHMEASURESPEC);
int modeheight = Measurespec.getmode (heightmeasurespec);
if (modewidth = = Measurespec.unspecified | | modewidth = = measurespec.at_most) {
Widthmeasurespec = Measurespec.makemeasurespec (Default_radio * 2, measurespec.exactly);
}
if (modeheight = = Measurespec.unspecified | | modeheight = = measurespec.at_most) {
Heightmeasurespec = Measurespec.makemeasurespec (Default_radio * 2, measurespec.exactly);
}
Super.onmeasure (Widthmeasurespec, Heightmeasurespec);
}
Then, when the layout changes, get the relevant coordinates and determine the initial center coordinates:
@Override
protected void onsizechanged (int w, int h, int oldw, int oldh) {
super.onsizechanged (W, H, OLDW, OLDH) ;
Circlex = (int) ((w) * 0.5 + 0.5);
Circley = (int) ((h) * 0.5 + 0.5);
}
The list file can then be configured like this:
<com.lovejjfg.circle.dragbubbleview
android:id= "@+id/dbv"
android:layout_width= "Wrap_content"
android:layout_height= "Wrap_content"
android:layout_gravity= "center"/>
After this, there will be a problem, that is, after the wrap_content, this view can be drawn to the area of its own so big, drag and drop are not seen! What about this pit, in fact, is very simple, the parent layout plus android:clipchildren= "false" Properties!
This pit is solved!!
Determination of the related state
We do not want it can be unlimited drag, that is, there is a drag the farthest distance, there is to let go after the return, burst. Accordingly, there are several states that need to be identified:
Private final static int state_idle = 1;//static state
private final static int state_drag_normal = 2;//dragging state
private F inal static int state_drag_break = Drag state after 3;//break
private final static int state_up_break = 4;//Let go of the burst state
private fi nal static int state_up_back = 5;//Let go of the state of return without break
private final static int state_up_drag_break_back = 6;//drag-and-drop fracture and return state c6/>private int currentstate = State_idle;
private int min_radio = (int) (Origin_radio * 0.4);//minimum radius
private int maxdistance = (int) (Min_radio * 13); The farthest drag-and-drop distance
After you've made sure of that, when you move, you're going to have to make a judgment:
STARTX = (int) ev.getx () when the case motionevent.action_move://moves;
Starty = (int) ev.gety ();
Updatepath ();
Invalidate ();
Break
private void Updatepath () {int dy = Math.Abs (circley-starty);
int dx = Math.Abs (CIRCLEX-STARTX);
Double dis = math.sqrt (dy * dy + dx * dx); if (dis <= maxdistance) {//increased, the original radius is reduced if (CurrentState = = State_drag_break | |
CurrentState = = state_up_drag_break_back) {currentstate = State_up_drag_break_back;
else {currentstate = State_drag_normal;
} Origin_radio = (int) (Default_radio-(dis/maxdistance) * (Default_radio-min_radio));
LOG.E (TAG, "Distance:" + (int) ((1-dis/maxdistance) * min_radio));
LOG.I (TAG, "Distance:" + origin_radio);
else {currentstate = State_drag_break;
}//distance = dis;
Flag = (Starty-circley) * (Startx-circlex) <= 0;
LOG.I ("TAG", "Updatepath:" + flag);
Angle = Math.atan (dy * 1.0/dx); }
The Updatepath () method has been seen before, and this one is complete.
The thing to do here is to change the associated state based on the drag distance, and to modify the radius of the original circle based on the percentage. There is the previous description of the determination of the relevant radians!
The last time to let go:
Case MOTIONEVENT.ACTION_UP:
if (currentstate = = state_drag_normal) {
currentstate = state_up_back;
Valuex.setintvalues (StartX, Circlex);
Valuey.setintvalues (Starty, Circley);
Animsetxy.start ();
} else if (CurrentState = = state_drag_break) {
currentstate = state_up_break;
Invalidate ();
} else {
currentstate = state_up_drag_break_back;
Valuex.setintvalues (StartX, Circlex);
Valuey.setintvalues (Starty, Circley);
Animsetxy.start ();
}
Break
Automatically returns the Valueanimator used here,
Animsetxy = new Animatorset ();
Valuex = Valueanimator.ofint (StartX, Circlex);
Valuey = Valueanimator.ofint (Starty, Circley);
Animsetxy.playtogether (Valuex, valuey);
Valuex.setduration ();
Valuey.setduration ();
Valuex.setinterpolator (New Overshootinterpolator ());
Valuey.setinterpolator (New Overshootinterpolator ());
Valuex.addupdatelistener (New Valueanimator.animatorupdatelistener () {
@Override public
Void Onanimationupdate (Valueanimator animation) {
startx = (int) animation.getanimatedvalue ();
LOG.E (TAG, "ONANIMATIONUPDATE-STARTX:" + startx);
Invalidate ();
}
);
Valuey.addupdatelistener (New Valueanimator.animatorupdatelistener () {
@Override public
Void Onanimationupdate (Valueanimator animation) {
starty = (int) animation.getanimatedvalue ();
LOG.E (TAG, "Onanimationupdate-starty:" + starty);
Invalidate ();
}
);
Finally, look at the complete OnDraw Method!
@Override protected void OnDraw (Canvas Canvas) {switch (currentstate) {case state_idle://idle state, draw the default circle if (Showcirc
Le) {canvas.drawcircle (Circlex, Circley, Origin_radio, paint);//default} break;
Case STATE_UP_BACK://Executes the returned animation case state_drag_normal://Drag state to draw a Bezier curve and a two round path.reset (); if (flag) {//First point Path.moveto ((float) (Circlex-math.sin (angle) * origin_radio), (float) (Circley-math.cos (Ang
Le) * origin_radio)); Path.quadto ((float) (startx + circlex) * 0.5), (float) (starty + Circley) * 0.5), (float) (Startx-math.sin (angle) * DR
Ag_radio), (float) (Starty-math.cos (angle) * drag_radio));
Path.lineto ((float) (startx + math.sin (angle) * drag_radio), (float) (Starty + math.cos (angle) * drag_radio)); Path.quadto ((float) (startx + circlex) * 0.5), (float) (starty + Circley) * 0.5), (float) (Circlex + math.sin (angle) * O
Rigin_radio), (float) (Circley + math.cos (angle) * origin_radio));
Path.close ();
Canvas.drawpath (path, paint);else {//First point Path.moveto ((float) (Circlex-math.sin (angle) * origin_radio), (float) (Circley + math.cos (angle)
* Origin_radio)); Path.quadto ((float) (startx + circlex) * 0.5), (float) (starty + Circley) * 0.5), (float) (Startx-math.sin (angle) * DR
Ag_radio), (float) (Starty + math.cos (angle) * drag_radio));
Path.lineto ((float) (startx + math.sin (angle) * drag_radio), (float) (Starty-math.cos (angle) * drag_radio)); Path.quadto ((float) (startx + circlex) * 0.5), (float) (starty + Circley) * 0.5), (float) (Circlex + math.sin (angle) * O
Rigin_radio), (float) (Circley-math.cos (angle) * origin_radio));
Path.close ();
Canvas.drawpath (path, paint); } if (showcircle) {canvas.drawcircle (Circlex, Circley, Origin_radio, paint);/default canvas.drawcircle (StartX = = 0? Circlex:startx, Starty = 0?
Circley:starty, Drag_radio, paint);//drag-and-drop} break; Case state_drag_break://dragged to the ceiling, drawing the dragged Circle: Case State_up_drag_break_back:if (showcircle) {canvas.drawcircle (startx = 0?) Circlex:startx, Starty = 0?
Circley:starty, Drag_radio, paint);//drag-and-drop} break;
Case state_up_break://paint a burst effect canvas.drawcircle (startX-25, startY-25, Circlepaint);
Canvas.drawcircle (StartX +, Starty +, circlepaint);
Canvas.drawcircle (StartX, startY-25, Circlepaint);
Canvas.drawcircle (StartX, Starty, Circlepaint);
Canvas.drawcircle (startX-25, Starty, Circlepaint);
Break
}
}
Here, the finished product came out!!
Summarize:
1, determine the default circular coordinates;
2, according to move situation, real-time get the latest coordinates, according to the moving distance (determine the angle), update the relevant state, draw the relevant path path. exceeds the upper limit and no longer draws the path path.
3, let go, according to the relevant state, either with the path to perform animation return, either without the path directly back, or directly burst!
The above is the use of Android Path to draw Bezier Curve example, follow-up to continue to supplement the relevant articles, thank you for your support for this site!