Implement QQ drag-and-drop removal by using the Android besell Curve
This is a learning Demo driven by curiosity. The effect is as follows:
Keys + rXE0Ke5 + keys + 7b7x/keys + s/keys + 0z8LNvKO6PGJyIC8 + DQo8aW1nIGFsdD0 = "here write picture description" src = "http://www.bkjia.com/uploads/allimg/160407/0413013240-1.png" title = "\"/>
The two edges with an arc are the three-point validation of a besell curve:
In Android, an interface has been provided to draw the besell curve. The effect is displayed after three points are passed in.
The beiser curve is a curve determined by three or more points. It plays an important role in graph image science, path also provides some methods for us to simulate the low-order besell curve.
The main purpose of this article is to learn about the besell curve and take a look at the use cases of the besell curve.
Case 1:
Case 2:
Case 3:
Case 4:
In fact, there are a lot more. QQ is the bubble that is dragged to clear messages. After reading this, you will be curious about how to do it.
I will not talk about the background here. I will only talk about some of the effects of the beiser curve. I will first steal images from Wikipedia to see the effects of drawing the beiser curve on different numbers of points.
The two points are drawn as straight lines, that is, the first-order besell curve.
Three points, second-order besell curve:
Four points, third-order besell curve:
Five Points, fourth-order besell curve:
Six points:
The figure above can be understood in this way, which is also a reference to the understanding of netizens. Both Level 1, level 2, and level 6 have at least two points, that is, the start point and the end point. Connect the two points with a rubber band. The points except the start and end points are equivalent to the points that are attractive to the rubber bands. They have different degrees of suction on the rubber bands due to different distance, and eventually reach a balance, therefore, the rubber band is offset to a non-starting point, for example:
Android provides an interface for drawing level 1, level 2, and level 3:
Level 1 interface:
public void lineTo(float x, float y)
Second-order interface:
public void quadTo(float x1, float y1, float x2, float y2)
Third-level interface:
public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
The top QQ drag and drop effect is drawn in second order.
The following describes how it works:
First, the 99 + message is an ImageView, Which is moved according to the OnTouch event. This is easy to understand. This is achieved through the constant setX () and setY () in the onTouch method.
Second: record start position p1 (x1, y1), record finger drag position p2 (x2, y2), with these two points we can draw a straight line, however, here we plot the Path with the Bessel curve, so the important task is to construct the Path
Third: Construct the Path, such:
After knowing the coordinates of two black spots and knowing the width Len of the green line, we can find the points in P1, P2, and P3, draw a coordinate system, and use the trigonometric function. Of course, this width Len is calculated dynamically based on the distance between two black spots, which is inversely proportional to the distance between the two black spots. To construct a Path, follow these steps:
Between p1-p3-p2: besell Curve
Between p2-p5: Straight Line
Between p5-p3-p4: besell Curve
Between p4-p1: Straight Line
Construct such a Path, and then draw the effect with a solid paint brush.
Of course, logical control is required to fully meet the requirements. The Code is as follows:
Package rander.com. betiller. betiller; 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. OS. handler; import android. OS. logoff; import android. util. attributeSet; import android. uti L. typedValue; import android. view. motionEvent; import android. view. view; import android. widget. frameLayout; import android. widget. imageView; import android. widget. toast; import rander.com. betiller. r;/*** Created by Rander. C on 16-4-3. */public class QQDragtoClearView extends FrameLayout {/*** maximum drag length */private final int DRAG_MAX_LEN = (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, 100, ge TResources (). getDisplayMetrics ();/*** default red dot radius size */private final int DEFAULT_RADIUS = 40;/*** background of the number of messages, which is an image in QQ, instead of drawing a quantity */private ImageView mIvMsgCountBg on the red background;/***** animation view when you drag and drop to clear */private ImageView mIvClearAnim;/***** hand-touched x, y coordinate */private float mTouchX; private float mTouchY;/*** initial x and y coordinates, default position */private float mStartX = 300; private float mStartY = 300;/*** the coordinate of the hand touch and the coordinate of the starting point * MCenterX = (mTouchX + mStartX)/2 * mCenterY = (mTouchY + mStartY)/2 * This is a vertex used to draw the besell curve */private float mCenterX; private float mCenterY; private static final int TOUCH_SLOP = 10;/*** Paint brush for the besell curve */private Paint mPaint;/*** Path for the besell curve */private Path mPath; /*** indicates whether the image is clicked */private boolean isTouch;/*** default radius size */private int mRaduis = DEFAULT_RADIUS; /*** determine whether the beiser curve is broken */private boolean I SBroken = false; public QQDragtoClearView (Context context) {this (context, null);} public QQDragtoClearView (Context context, AttributeSet attrs) {this (context, attrs,-1 );} public QQDragtoClearView (Context context, AttributeSet attrs, int defStyleAttr) {super (context, attrs, defStyleAttr); init () ;}/ *** initialize */private void init () {mPath = new Path (); mPaint = new Paint (); mPaint. setAntiAlia S (true); mPaint. setStyle (Paint. style. FILL_AND_STROKE); mPaint. setStrokeWidth (2); mPaint. setColor (Color. RED); FrameLayout. layoutParams params = new FrameLayout. layoutParams (60, 60); mIvClearAnim = new ImageView (getContext (); mIvClearAnim. setLayoutParams (params); mIvClearAnim. setImageResource (R. drawable. tip_anim); mIvClearAnim. setVisibility (INVISIBLE); mIvMsgCountBg = new ImageView (getContext (); m IvMsgCountBg. setLayoutParams (params); mIvMsgCountBg. setImageResource (R. drawable. skin_tips_newmessage_ninetynine); mIvMsgCountBg. setVisibility (VISIBLE); addView (mIvClearAnim); addView (mIvMsgCountBg) ;}@ Override public boolean onTouchEvent (MotionEvent event) {mTouchX = (int) event. getX (); mTouchY = (int) event. getY (); switch (event. getAction () {case MotionEvent. ACTION_DOWN: // ACTION_DOWN is used to determine the trigger Rect rect = new Rect (); int [] location = new int [2]; mIvMsgCountBg. getDrawingRect (rect); mIvMsgCountBg. 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 () {// If isTouch is true, it indicates that the painting starts. isTouch = true;} break; case MotionEven T. ACTION_UP: case MotionEvent. ACTION_CANCEL: // when lifted up, first judge whether it is disconnected before starting the animation, and the number of messages is prompted to disappear // if not disconnected, return to the origin if (isBroken) {boolean isPositionNoChanged = false; // if the starting position is not much different from the starting position, return to the starting position without disappearing if (Math. abs (mTouchX-mStartX) <TOUCH_SLOP & Math. abs (mTouchY-mStartY) <TOUCH_SLOP) {isPositionNoChanged = true;} if (isPositionNoChanged) {break;} else {mIvMsgCountBg. setVisibility (INVISIBLE); mIvClearAnim. set Visibility (View. VISIBLE); mIvClearAnim. setX (mTouchX-mIvClearAnim. getWidth ()/2); mIvClearAnim. setY (mTouchY-mIvClearAnim. getHeight ()/2); mIvClearAnim. setImageResource (R. drawable. tip_anim); (AnimationDrawable) mIvClearAnim. getDrawable ()). stop (); (AnimationDrawable) mIvClearAnim. getDrawable ()). start (); mPath. reset (); new Handler (). postDelayed (new Runnable () {@ Override public void run () {mI VMsgCountBg. setVisibility (VISIBLE) ;}}, 1200) ;}isbroken = false ;}istouch = false; mIvMsgCountBg. setX (mStartX-mIvMsgCountBg. getWidth ()/2); mIvMsgCountBg. setY (mStartY-mIvMsgCountBg. getHeight ()/2); break;} mCenterX = (mTouchX + mStartX)/2; mCenterY = (mTouchY + mStartY)/2; if (isTouch) {mIvMsgCountBg. setX (mTouchX-mIvMsgCountBg. getWidth ()/2); mIvMsgCountBg. setY (mTouchY-mIvMsgC OuntBg. getHeight ()/2);} invalidateView (); return true;} public void invalidateView () {if (loheight. mylogoff () = logoff. getmainlodate () {invalidate () ;}else {postInvalidate () ;}@ Override protected void onLayout (boolean changed, int l, int t, int r, int B) {mIvClearAnim. setX (mStartX); mIvClearAnim. setY (mStartY); mIvMsgCountBg. setX (mStartX-mIvMsgCountBg. getWidth ()/2); mIvMsgCountBg. se TY (mStartY-mIvMsgCountBg. getHeight ()/2); super. onLayout (changed, l, t, r, B) ;}@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); // if it is in touch and the line is not broken, draw the canvas again. if (isTouch &&! IsBroken) {checkDragLen (); canvas. drawPath (mPath, mPaint); canvas. drawColor (Color. TRANSPARENT, PorterDuff. mode. OVERLAY); canvas. drawCircle (mStartX, mStartY, mRaduis, mPaint); canvas. drawCircle (mTouchX, mTouchY, mRaduis, mPaint);} else {// It is equivalent to drawing the canvas clearly. drawCircle (mStartX, mStartY, 0, mPaint); canvas. drawCircle (mTouchX, mTouchY, 0, mPaint); canvas. drawLine (0, 0, 0, 0, mPaint); canvas. drawColor (Color. TRANSPARENT, PorterDuff. mode. OVERLAY);}/*** check whether the drag length is enough. If the drag length is exceeded, the system disconnects and does not draw */private void checkDragLen () {int len = (int) Math. sqrt (Math. pow (mTouchX-mStartX, 2) + Math. pow (mTouchY-mStartY, 2); mRaduis =-len/15 + DEFAULT_RADIUS; // If the length exceeds the maximum length, set isBroken to true, in the future, if (len> DRAG_MAX_LEN) {isBroken = true; return;} // the four points float offsetX = (float) required to draw the besell curve will not be drawn) (mRaduis * Math. sin (Math. atan (mTouchY-mStartY)/(mTouchX-mStartX); float offsetY = (float) (mRaduis * Math. cos (Math. atan (mTouchY-mStartY)/(mTouchX-mStartX); float x1 = mStartX-offsetX; float y1 = mStartY + offsetY; float x2 = mTouchX-offsetX; float y2 = mTouchY + offsetY; float x3 = mTouchX + offsetX; float y3 = mTouchY-offsetY; float x4 = mStartX + offsetX; float y4 = mStartY-offsetY; mPath. reset (); mPath. moveTo (x1, y1); mPath. quadTo (mCenterX, mCenterY, x2, y2); mPath. lineTo (x3, y3); mPath. quadTo (mCenterX, mCenterY, x4, y4); mPath. lineTo (x1, y1 );}}
Use activity_main.xml
Activity
package rander.com.bezier;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
The effect is the above solid-line effect.