Bezierdemo Source code Analysis-realize the effect of QQ message bubble drag and drop disappear

Source: Internet
Author: User
Tags drawtext gety

In this article we compared the Draggableflagview and Bezierdemo two project differences, mentioned will be one of the source code analysis, then we will analyze the Bezierdemo source bar, because the project's source code is the simplest, you can more directly analyze the core of things. But the effect is still draggableflagview better. I try to speak more carefully and meet more beginners. This article focuses on the implementation of tensile effects.

SOURCE structure

Bezierdemo only two Java files

The Mainactivity.java is the program interface, and Bezierview.java is the class that implements the adhesion stretching effect.

Mainactivity.java

Package Github.chenupt.bezier;import Android.app.activity;import Android.os.bundle;public class MainActivity extends Activity {    @Override    protected void onCreate (Bundle savedinstancestate) {        super.oncreate ( Savedinstancestate);        Setcontentview (R.layout.activity_main);    }}

Activity_main.xml

<linearlayout xmlns:android= "http://schemas.android.com/apk/res/android"    xmlns:tools= "http// Schemas.android.com/tools "    android:layout_width=" match_parent "    android:layout_height=" Match_parent "    android:orientation= "vertical"    tools:context= ". Mainactivity ">    <github.chenupt.bezier.bezierview        android:layout_width=" Match_parent "        Android : layout_height= "match_parent"        android:background= "@android: Color/transparent"/></linearlayout>

Here's a question: Why the Bezierview control's layout_width and Layout_height are match_parent. This is because this code is very rough, haha.


Well, as you can see from the activity above, all the functions are implemented by the Bezierview control, so we turn directly to Bezierview.java

Post Code First

Package Github.chenupt.bezier;import Android.content.context;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.graphics.path;import Android.graphics.porterduff;import Android.graphics.rect;import android.graphics.drawable.AnimationDrawable; Import Android.util.attributeset;import Android.view.motionevent;import Android.view.view;import Android.view.viewgroup;import android.widget.framelayout;import android.widget.imageview;/** * Created by [email  protected] on 11/20/14. * Description:custom layout to draw Bézier */public class Bezierview extends Framelayout {//default fixed point circle radius public Stati    C Final float Default_radius = 20;    private paint paint;    private path Path;    gesture coordinate float x = 300;    Float y = 300;    Anchor point coordinate float Anchorx = 200;    float anchory = 300;    Start coordinate float StartX = 100;    float starty = 100;    Fixed-point circle radius float radius = Default_radius;    Determines whether the animation starts with a Boolean isanimstart; Determine whether to start dragging    Boolean Istouch;    ImageView Exploredimageview;    ImageView Tipimageview;        Public Bezierview (Context context) {super (context);    Init ();        } public Bezierview (context context, AttributeSet Attrs) {Super (context, attrs);    Init (); } public Bezierview (context context, AttributeSet attrs, int defstyleattr) {Super (context, attrs, defstyleattr)        ;    Init ();        } private void Init () {path = new path ();        Paint = new paint ();        Paint.setantialias (TRUE);        Paint.setstyle (Paint.Style.FILL_AND_STROKE);        Paint.setstrokewidth (2);        Paint.setcolor (color.red);        Layoutparams params = new Layoutparams (ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);        Exploredimageview = new ImageView (GetContext ());        Exploredimageview.setlayoutparams (params);        Exploredimageview.setimageresource (R.drawable.tip_anim);        Exploredimageview.setvisibility (view.invisible); TiPimageview = new ImageView (GetContext ());        Tipimageview.setlayoutparams (params);        Tipimageview.setimageresource (R.drawable.skin_tips_newmessage_ninetynine);        AddView (Tipimageview);    AddView (Exploredimageview); } @Override protected void OnLayout (Boolean changed, int left, int top, int. right, int bottom) {Exploredimag        Eview.setx (Startx-exploredimageview.getwidth ()/2);        Exploredimageview.sety (Starty-exploredimageview.getheight ()/2);        Tipimageview.setx (Startx-tipimageview.getwidth ()/2);        Tipimageview.sety (Starty-tipimageview.getheight ()/2);    Super.onlayout (changed, left, top, right, bottom);        } private void Calculate () {Float distance = (float) math.sqrt (Math.pow (Y-starty, 2) + Math.pow (X-STARTX, 2));        radius =-distance/15+default_radius;            if (RADIUS < 9) {Isanimstart = true;            Exploredimageview.setvisibility (view.visible); Exploredimageview.setimageresource (R.Drawable.tip_anim);            ((animationdrawable) exploredimageview.getdrawable ()). Stop ();            ((animationdrawable) exploredimageview.getdrawable ()). Start ();        Tipimageview.setvisibility (View.gone);        }//According to the angle of the four-sided four points float OffsetX = (float) (Radius*math.sin (Math.atan (y-starty)/(X-STARTX)));        float OffsetY = (float) (Radius*math.cos (Math.atan (y-starty)/(X-STARTX)));        float x1 = startx-offsetx;        Float y1 = starty + OffsetY;        float x2 = x-offsetx;        float y2 = y + OffsetY;        Float x3 = x + OffsetX;        float y3 = y-offsety;        Float x4 = StartX + OffsetX;        float y4 = starty-offsety;        Path.reset ();        Path.moveto (x1, y1);        Path.quadto (Anchorx, anchory, x2, y2);        Path.lineto (x3, y3);        Path.quadto (Anchorx, Anchory, X4, Y4);        Path.lineto (x1, y1);        Change the position of the icon Tipimageview.setx (X-tipimageview.getwidth ()/2); Tipimageview.sety (y-tIpimageview.getheight ()/2); } @Override protected void OnDraw (canvas canvas) {if (Isanimstart | |!istouch) {canvas.drawcolor (Co Lor.        TRANSPARENT, PorterDuff.Mode.OVERLAY);            }else{calculate ();            Canvas.drawcolor (Color.transparent, PorterDuff.Mode.OVERLAY);            Canvas.drawpath (path, paint);            Canvas.drawcircle (StartX, starty, radius, paint);        Canvas.drawcircle (x, y, radius, paint);    } super.ondraw (canvas); } @Override public boolean ontouchevent (Motionevent event) {if (event.getaction () = = Motionevent.action_down)            {//Determine if the touch point is in tipimageview rect rect = new rect ();            int[] location = new INT[2];            Tipimageview.getdrawingrect (rect);            Tipimageview.getlocationonscreen (location);            Rect.left = location[0];            Rect.top = location[1];            Rect.right = Rect.right + location[0]; Rect.bottom = Rect.bottom + locATION[1];            if (rect.contains (int) event.getrawx (), (int) Event.getrawy ())) {Istouch = true;            }}else if (event.getaction () = = Motionevent.action_up | | event.getaction () = = Motionevent.action_cancel) {            Istouch = false;            Tipimageview.setx (Startx-tipimageview.getwidth ()/2);        Tipimageview.sety (Starty-tipimageview.getheight ()/2);        } invalidate ();        if (Isanimstart) {return super.ontouchevent (event);        } Anchorx = (Event.getx () + StartX)/2;        Anchory = (event.gety () + starty)/2;        x = Event.getx ();        y = event.gety ();    return true; }}

The control is a custom framelayout, so you can add a picture that displays the number of messages directly without having to customize the view.

That part of the comment about the member variable is quite clear, I'll look directly at

Init () method

The brush paint is first initialized in the Init method, which is the paint that draws the adhesion stretch effect. The paint initialization code then adds two images to Framelayout: Exploredimageview and Tipimageview,exploredimageview are the bubbles that appear after the pull-off, And Tipimageview is a digital hint, these two imageview are just to help imitate QQ, but not the core of our discussion.

OnLayout () method

Non-focused, slightly.

Calculate () method

This is the method of calculating the coordinates according to the position of the finger dragging, and also here the path path is defined in terms of the coordinate points:

        Path.reset ();        Path.moveto (x1, y1);        Path.quadto (Anchorx, anchory, x2, y2);        Path.lineto (x3, y3);        Path.quadto (Anchorx, Anchory, X4, y4);        Path.lineto (x1, y1);

This side code is the core of the adhesion stretch effect. A while, and we do all kinds of experiments are here to fix change.

OnDraw () method

    @Override    protected void OnDraw (canvas canvas) {        if (Isanimstart | |!istouch) {            Canvas.drawcolor ( Color.transparent, PorterDuff.Mode.OVERLAY);        } else{            Calculate ();            Canvas.drawcolor (Color.transparent, PorterDuff.Mode.OVERLAY);            Canvas.drawpath (path, paint);           Canvas.drawcircle (StartX, starty, radius, paint);           Canvas.drawcircle (x, y, radius, paint);        }        Super.ondraw (canvas);    }

This method invokes the calculate method above, and then draws the path and circle based on the computed value.

Ontouchevent () method

This method will record the necessary location information according to the position change of the touch point, for the Calculate () method to calculate, and send the draw request where necessary.


Step by Step decomposition

If it ends here, you're not satisfied-"I still don't understand how the Bezier curve is applied to it." To thoroughly understand that we are going to do several experiments of decomposing the code.


First we find the OnDraw method,

    @Override    protected void OnDraw (canvas canvas) {        if (Isanimstart | |!istouch) {            Canvas.drawcolor ( Color.transparent, PorterDuff.Mode.OVERLAY);        } else{            Calculate ();            Canvas.drawcolor (Color.transparent, PorterDuff.Mode.OVERLAY);            Canvas.drawpath (path, paint);            Canvas.drawcircle (StartX, starty, radius, paint);            Canvas.drawcircle (x, y, radius, paint);        }        Super.ondraw (canvas);    }

In

if (Isanimstart | |!istouch) {

The code in is the effect after the pull-off, do not care about him.

Main look at the code in else

The Calculate () method is called first, and then the

Canvas.drawcolor (Color.transparent, PorterDuff.Mode.OVERLAY);

It doesn't matter if this is removed.

A closed path with a Bezier curve is then drawn:

Canvas.drawpath (path, paint);

And then draw the circles at each end.


In order to see the effect more intuitively, we will

Default fixed point circle radius
public static final Float Default_radius = 20;

Change into

Default fixed point circle radius
public static final float Default_radius = 150;

This larger point will see the stretching process more clearly, and the pull is very long and will not break, the breaking point is determined by the following code:

In the calculate method

float distance = (float) math.sqrt (Math.pow (Y-starty, 2) + Math.pow (X-STARTX, 2));
radius =-distance/15+default_radius;

if (RADIUS < 9) {
Isanimstart = true;

The results are as follows:

You see, I've pulled half the screen.

However, it is still difficult to see how the curve is drawn because the paint type of the brush paint is in fill mode and we change it to line mode:

Change the init () method to

private void init () {
Path = new Path ();

Paint = new paint ();
Paint.setantialias (TRUE);
Paint.setstyle (Paint.Style.STROKE);
Paint.setstrokewidth (2);
Paint.setcolor (color.red);

......

So we can see how the lines are assembled:


It can be seen that two circles and a closed path are indeed composed. That digital picture is a bit of an eyesore, we're trying to remove


In the appropriate location of the calculate () method, add

Tipimageview.setvisibility (View.gone);

I am in the third row or so, in short, can be guaranteed to be executed on the line. I dare not say it is the most suitable, I just want to get rid of it.

Here is a transform graph that is stripped back and forth:

A little creepy ....

Now let's get rid of the two circles, and these two circles are only changing the lower radius based on the size of the distance between two points (the second point also changes the dot coordinates). In the middle part of the Bezier curve, let's look at the true face of the path that contains the Bezier curve.

To remove a circle, simply comment out the relevant code for the OnDraw method:

Here is the effect after the comment:

This is our path.

Back to the code that builds this path, in the Calculate method:

        Path.reset ();        Path.moveto (x1, y1);               Path.quadto (Anchorx, anchory, x2, y2);        Path.lineto (x3, y3);        Path.quadto (Anchorx, Anchory, X4, y4);             Path.lineto (x1, y1);

The LineTo method is to draw a straight line, the Quadto method is to draw the Bezier curve, exactly, is to draw the quadratic Bezier curve. In order to be able to see the order of path, we define separately

(x1, y1) for Point A

(x2, y2) for Point B

(x3, y3) for C point

(x4, Y4) for D-Point

(Anchorx, Anchory) is the x point, which is the control point of the quadratic Bezier curve, where there are two quadratic Bezier curves, all of which are the same control points.

At the same time in the canvas to mark the letters of these points, the specific practice is to call Canvas.drawtext, modify the specific code I will not send.

The display position of each point is biased (especially at x Point), because the Canvas.drawtext parameters need to be adjusted according to the size of the characters, I do not do it for the sake of simplicity, but these points you should know their actual position, A,b,c,d is very well identified, but X should be in the middle.

With the picture above, it's a good idea for this piece of code.

        Path.moveto (x1, y1);               Path.quadto (Anchorx, anchory, x2, y2);        Path.lineto (x3, y3);        Path.quadto (Anchorx, Anchory, X4, y4);             Path.lineto (x1, y1);

The tensile adhesion effect depends mainly on the two Bezier curves drawn by the quadto, which take the middle position between them as control points, causing the curve to bend inward in the same radian. As the circles on both ends grow longer, the position of the control points and the ends of the two curves change (the position of the endpoints and control points need to be calculated from the distance) to form the adhesive effect of the rubber bands.


Calculation of each coordinate point

So the last question now is how to find the point of these changes.

First we need to record the movement of the finger, the change of touch Point, in the demo is used (x, y) to represent the touch point, and then according to (Startx,starty) (this point is written dead) calculate the coordinates of the control point (anchorx,anchory)

The code is as follows

    @Override public boolean ontouchevent (Motionevent event) {if (event.getaction () = = Motionevent.action_down) {            Determines whether the touch point is in tipimageview rect rect = new rect ();            int[] location = new INT[2];            Tipimageview.getdrawingrect (rect);            Tipimageview.getlocationonscreen (location);            Rect.left = location[0];            Rect.top = location[1];            Rect.right = Rect.right + location[0];            Rect.bottom = Rect.bottom + location[1];            if (rect.contains (int) event.getrawx (), (int) Event.getrawy ())) {Istouch = true;            }}else if (event.getaction () = = Motionevent.action_up | | event.getaction () = = Motionevent.action_cancel) {            Istouch = false;            Tipimageview.setx (Startx-tipimageview.getwidth ()/2);        Tipimageview.sety (Starty-tipimageview.getheight ()/2);        } invalidate ();   if (Isanimstart) {return super.ontouchevent (event);     } Anchorx = (Event.getx () + StartX)/2;        Anchory = (event.gety () + starty)/2;        x = Event.getx ();        y = event.gety ();    return true; }


Where the code in the IF and else code blocks is unrelated to the adhesion effect, the code is about the ImageView display and disappearance of the bubbles.

The main thing is the following code

        Invalidate ();        if (Isanimstart) {            return super.ontouchevent (event);        }        Anchorx =  (Event.getx () + StartX)/2;        Anchory =  (event.gety () + starty)/2;        x =  event.getx ();        y =  event.gety ();

It can be seen in the ontouchevent, the main work is recorded, sitting punctuation calculation or in the Calculate () method (but here also simple calculation of the control point coordinates (anchorx,anchory), in fact, this can also be put into the calculate inside). Other than that

Invalidate () method I think it's better to put it in the end. But nothing serious, that is, a little behind a point, you do not feel at all.


and the Calculate () method in the face of the coordinates of the calculation is very simple, not a few lines of code, combined with the above several pictures should be easy to solve. We will not repeat it here.



In fact, the whole article can be summed up in one sentence: The key to the adhesion effect is the same control point (intermediate points) "stall" two Bezier curve.

Finally do a little to add, in order to make the effect of the eraser more realistic, the demo also dynamically changed the radius of the two ends of the dot, of course, this will also lead to other points also make corresponding changes

        float distance = (float) math.sqrt (Math.pow (Y-starty, 2) + Math.pow (X-STARTX, 2));        radius =-distance/15+default_radius;

Original http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0311/2577.html


Bezierdemo Source code Analysis-realize the effect of QQ message bubble drag and drop disappear

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.