[Open-Source Project Analysis] Analysis of QQ's "one-click off-duty" function-learning the basic use of the Path and the besell curve, path besell
A long time ago, QQ implemented the "one-click Work" function. What is "one-click Work "? When you have information on QQ, there will be a red dot indicating the number of information in the lower part. After clicking and dragging, the "one-click off" effect will appear. This article will introduce the basic implementation of this function in conjunction with a simple implementation of this function on github.
Project address
Https://github.com/chenupt/BezierDemo
Final Effect
Implementation Principle
I personally feel that this effect is very beautiful! Let's look at the implementation principle ~
Note: See the project source code for the following content.
In fact, from the code perspective, the implementation process is not complicated, and the key point is
- Path usage
- Use of the besell curve.
The core of this project is BezierView, which is inherited from FrameLayout. When dragging, it is equivalent to covering the screen. The init () method mainly performs the following operations:
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); }
Initialize the Path and Paint objects, and then dynamically generate two imageviews.
- ExploredImageView is mainly used to achieve explosive effect, which is invisible by default.
- The red icon when the tipImageView finger is dragged
The image resource set by exploredImageView is an AnimationDrawable. The following is the declaration in res, which controls the playing sequence and duration of each image.
<?xml version="1.0" encoding="utf-8"?><animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true"> <item android:drawable="@drawable/idp" android:duration="300"/> <item android:drawable="@drawable/idq" android:duration="300"/> <item android:drawable="@drawable/idr" android:duration="300"/> <item android:drawable="@drawable/ids" android:duration="300"/> <item android:drawable="@drawable/idt" android:duration="300"/> <item android:drawable="@android:color/transparent" android:duration="300"/></animation-list>
When learning this custom control, we can focus on viewing the code according to the View rendering process. For example, we can
In the following order, we will learn this project.
- OnMeasure ()
- OnLayout ()
- OnDraw ()
- OnTouchEvent ()
Because this project has not rewritten onMeasure (), we can directly look at what we have done from onLayout.
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { exploredImageView.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); }
The code is still very understandable. It is nothing more than initializing the location of the ImageView. Here there are two variables, startX and startY, which control the initialization coordinates of the red points, it will not change throughout the process.
What about onDraw?
@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); }
The operations in onDraw () are not complex. If an animation is being executed or is not in the touch mode, draw a transparent color. Otherwise, draw a real interface. The calculate () method is a key point. From the perspective of naming, it should be to calculate some coordinate values, and then draw two circles, the coordinates of the two circles, one is (startX, startY), the other is (x, y), the color and radius are the same, this is to simplify the calculation, so set the radius of the two circles to the same.
Let's take a look at the operations in onTouchEvent ().
@ Override public boolean onTouchEvent (MotionEvent event) {if (event. getAction () = MotionEvent. ACTION_DOWN) {// determines whether the touch point is in the 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 ;}
First, the screen Coordinate Position of tipImageView is obtained during the press, and then the touch point position is used to determine whether it is the touch state, thus changing the value of isTouch. If it is not the press time, then, the isTouch is changed to touch the status and restore the location of the tipImageVIew. However, in any case, we will execute invalidate () to call onDraw (), where we will execute the actual circle operation, which we will be watching later. The next step is to update the x and y coordinates based on whether the animation is playing, as well as the anchorX and anchorY values.
The value of x and y is actually the position of the touch point. It is mainly used to control the position of the circle pressed by the finger. What about anchorX and anchorY? These two values are actually coordinates of the control anchor and used to draw the besell curve.
Here, I believe you should understand the basic idea of implementation, but the most important thing is to pull the results. How is it actually implemented? So let's take a look at what the most important calculate () has done!
Before reading this code, let's take a simple look at the besell curve and how to plot it.
In 1962, the bersel curve was first studied and used by the French mathematician Pierre béz, and a detailed formula was provided. The So curve was also named by his name.
The quadTo method given in Path belongs to the second-order besell curve.
Let's take a look at the GIF motion picture without any code. I stole it from your brother. Don't tell him ^_^.
From the above motion graph, we can find that we can draw a smooth curve with only three points. P0 and P2 are the start and end points, p1 is our anchorX and anchorY mentioned above.
The question is, how many points do we need to know if we want to achieve this drag and drop stretch effect?
A design drawing first
It can be seen that in the design drawing, there are four coordinate points of the P1-P4, is the coordinates of the two outer tangent of the circle and the point of the circle, because the P1-P2 and the P3-P4 of the two besell curve distortion degree is the same, therefore, the anchor only needs to take a point on the line connecting P0 to the origin coordinate. Therefore, we need five coordinate points.
Let me have a drink ^_^
Come and come, let's continue!
Now that we know which five coordinate points are required, anchorX and anchorY have already been calculated in onTouchEvent (). How can we find the remaining four coordinate points? In fact, this is the main internal work done by calculate.
Since the radius of the two circles is set to the same, the computation can be simplified, so the following code assumes that the radius of the two circles is the same, kaizige will give you a hand-drawn high-definition uncoded big image.
StartX and startY are specified values. Here we use it as the coordinate origin, and the coordinates of the other circle are (x, y), that is, the coordinates of the point touched by the finger. The radius of the two circles is the same, the outer tangent is parallel, and the vertical line is perpendicular to the two tangent points after (x, y.
Now, we know (startX, startY), (x, y), radius, and there is a right angle. Therefore, we only need to know an angle, and then we can find offsetX and offsetY, the four-point coordinates of P1-P4 are also obtained ~~~
Is this a good idea?
Simple. Let's take a big picture with no code in HD ~
Because
- Alpha = Alpha 3
- Limit 3 + limit 2 = 90
- Limit 1 + limit 2 = 90
So
Alpha = alpha 1
Is this a three-step Junior High School... Forgot
So how can I find limit 1? Easy, (x, y) knows all about it,
Tan 1_1 = (y-startY)/(x-startX );
Therefore
Counter 1 = arctan (y-startY)/(x-startX ))
Do you know the angle and radius? Can't you find offsetX and offsetY ~
So
float offsetX = (float) (radius*Math.sin(Math.atan((y - startY) / (x - startX))));float offsetY = (float) (radius*Math.cos(Math.atan((y - startY) / (x - startX))));
So .,, Now let's look at the following code. Do you still say you don't understand it?
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) implements edimageview. getDrawable ()). stop (); (AnimationDrawable) implements edimageview. getDrawable ()). start (); tipImageView. setVisibility (View. GONE);} // calculate the 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 );}
Work out the coordinates of the four points and know the position of the anchor. Use the path to connect it.
Ele. Me, this article is here to go to dinner
Related Projects and articles
- Implementation of red dot drag and drop elimination for mobile phone QQ5.0
- Qq mobile 5.0 "one-click Work" design summary
Related Projects
-
Respect Original, reprint Please note: From kaizi brother (http://blog.csdn.net/zhaokaiqiang1992) infringement must be studied!
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.