Custom View learning notes: Path-based learning notes: learning notes
I. besell curve Source
In the field of Numerical Analysis of mathematics, the besell curve is a very important parameter curve in computer graphics. A higher dimension is called the besell curve. The besell triangle is a special example.
These two articles are the most clear explanations I have found so far. You can observe and study them.
Ii. Bay sale curve overview in Android
1. First-order besell curve:
Principle: there is no control point, and there are only two data points (A and B). The final result is A line segment. It is actually the lineTo discussed previously.
2. Second-Order besell curve:
Principle: The curve state is described by two data points (determining the start and end positions of the curve) and one control point (determining the bending degree of the curve). The corresponding method is as follows:
public void quadTo(float x1, float y1, float x2, float y2);public void rQuadTo(float dx1, float dy1, float dx2, float dy2);
3. Third-Order besell curve:
Principle: two data points (determining the starting and ending positions of the curve) and two control points (determining the bending degree and status of the curve) are used to describe the curve state. The corresponding method is as follows:
public void cubicTo(float x1,float y1,float x2,float y2,float x3,float y3);public void rCubicTo(float x1,float y1,float x2,float y2,float x3,float y3);
Here, the data points and control points are described as follows:
Compared with a second-order curve, a third-order curve can produce more complex shapes. However, for a higher-order curve, a lower-order curve combination can achieve the same effect. Therefore, our Encapsulation Method for The besell curve is generally as high as the third-order curve.
Instructions on downgrading and upgrading:
Iii. Use of second-order besell Curves
1. Use of the second-order besell curve quadTo:
public void quadTo(float x1, float y1, float x2, float y2);
(X1, y1) is the coordinate of the control point, (x2, y2) is the coordinate of the end point. Q: What is the starting point when there is a coordinate between the control point and the end point?
Answer:
Entire LineStart PointIs through Path. moveTo (x, y) is specified. If we call quadTo () consecutively, the end point of the first quadTo () is the start point of the next quadTo () function; if Path. if moveTo (x, y) is used to specify the start point, the upper left corner (0, 0) of the control is used as the default start point. (moveTo (x, y) is a vertex, plus quadTo (x1, y1, x2, y2) two vertices, three vertices in total .)
2. Sample Code:
Public class bezr extends View {private Paint mPaint; private int centerX, centerY; private PointF start, end, control; public bezr (Context context) {super (context ); mPaint = new Paint (); mPaint. setColor (Color. BLACK); mPaint. setStrokeWidth (8); mPaint. setStyle (Paint. style. STROKE); mPaint. setTextSize (60); start = new PointF (0, 0); end = new PointF (0, 0); control = new PointF (0, 0);} public bezr (Context context Context, AttributeSet attrs) {super (context, attrs) ;}@ Override protected void onSizeChanged (int w, int h, int oldw, int oldh) {super. onSizeChanged (w, h, oldw, oldh); centerX = w/2; centerY = h/2; // initialize the start of the data point and control point. x = centerx- 200; start. y = centerY; end. x = centerX + 200; end. y = centerY; control. x = centerX; control. y = centerY-100;} @ Override public boolean onTouchEvent (MotionEvent event) {// update the control point based on the Touch Location and prompt to re-paint the control. x = event. getX (); control. y = event. getY (); invalidate (); return true ;}@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); // draw the data point and Control Point mPaint. setColor (Color. GRAY); mPaint. setStrokeWidth (20); // draw three points: start, end, and control canvas. drawPoint (start. x, start. y, mPaint); canvas. drawPoint (end. x, end. y, mPaint); canvas. drawPoint (control. x, control. y, mPaint); // draw the Auxiliary Line mPaint. setStrokeWidth (4); canvas. drawLine (start. x, start. y, control. x, control. y, mPaint); canvas. drawLine (end. x, end. y, control. x, control. y, mPaint); // draw the mPaint of the besell curve. setColor (Color. RED); mPaint. setStrokeWidth (8); Path path = new Path (); // path. moveTo starts with the start point, corresponding path. quadTo ends with an end point; opposite; // if it starts with an end point, the corresponding path. quadTo ends with start. Otherwise, the curve cannot be drawn (control. x, control. y) is the coordinate of the control point, (end. x, end. y) is the end coordinate // the starting point of the entire line is through Path. moveTo (start. x, start. y) to specify the path. moveTo (start. x, start. y); path. quadTo (control. x, control. y, end. x, end. y); canvas. drawPath (path, mPaint );}}
3. Problems with Path. lineTo:
When Path. lineTo () is used to draw a graph, especially a graph with rounded corner radians. When the corner or arc appears, there will be obvious mosaic-like sertices and other things, which are not smooth at all, it looks uncomfortable, especially when the image is big. This requires optimization to achieve smooth transition between lines. There are two optimization schemes:
First, set mPaint. setAntiAlias (true); // anti-aliasing; second, use the Path. quadTo function of the second-order besell curve to re-implement the moving track effect.
The two solutions can be combined for better results.
Public class FingerPath extends View {private Paint mPaint; private Path mPath; private float mPreX, mPreY; // true: Call the LineTo method; false: Call the QuadTo method private boolean lineOrQuad = true; public FingerPath (Context context) {super (context); if (mPaint = null) {mPaint = new Paint (); mPaint. setStrokeWidth (20); mPaint. setAntiAlias (true); // anti-sawtooth mPaint. setColor (Color. GREEN); mPaint. setStyle (Paint. style. STROKE); mP Ath = new Path () ;}@ Override public boolean onTouchEvent (MotionEvent event) {switch (event. getAction () {case MotionEvent. ACTION_DOWN: {if (lineOrQuad) {mPath. moveTo (event. getX (), event. getY ();} else {mPath. moveTo (event. getX (), event. getY (); // mPreX, mPreY indicates the first point of the finger mPreX = event. getX (); mPreY = event. getY ();} return true;} case MotionEvent. ACTION_MOVE: if (lineOrQuad) {mPath. lineTo (event. get X (), event. getY ();} else {// The reason for dividing by 2: mPreX/mPreY is actually very close to event. getX ()/event. for getY (), divide by 2 to take the average value, so that the fluctuation between each point is smaller. Float endX = (mPreX + event. getX ()/2; float endY = (mPreY + event. getY ()/2; mPath. quadTo (mPreX, mPreY, endX, endY); // mPreX, mPreY indicates the previous point of the finger mPreX = event. getX (); mPreY = event. getY () ;}// Note: postInvalidate () is used here, not Invalidate (); it is related to thread security. // Invalidate () must be executed in the UI thread, otherwise, an error will be reported. postInvalidate () is not so special. It can be executed in any thread without having to be the main thread. In fact, postInvalidate () yes // use handler to send a message to the main thread to refresh the interface, so it can be executed in any thread. When the interface is refreshed, postInvalidate () is not as fast as Invalidate. Invalidate (); break; default: break;} return super. onTouchEvent (event) ;}@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); canvas. drawColor (Color. GRAY); canvas. drawPath (mPath, mPaint);} public void reset () {mPath. reset (); postInvalidate ();} public void setQuadOrLine (boolean quadOrLine) {this. lineOrQuad = quadOrLine ;}}
When ACTION_DOWN, use mPath. moveTo (event. getX (), event. getY () sets the initial position of Path to the contact point of the finger. If you do not call mPath. if moveTo is used, it starts from (0, 0) by default. Then we define two variables mPreX, mPreY, to represent the first point of the finger. Through the above analysis, we know that this point is used for control points. Finally, return true to let the ACTION_MOVE and ACTION_UP events continue to be passed to this control.
In ACTION_MOVE, we first find the end point. We say that the end point is the middle position of the line segment, so it is easy to find its coordinates endX and endY; the control point is the position of the last finger, namely mPreX and mPreY. Some may ask, where is the starting point. The first starting point is Path. moveTo (x, y) is defined. For other parts, the end point of a quadTo is the starting point of the next quadTo. So the starting point here is the center point of the previous line segment. The center point of each line segment is used as the start point and end point, and the finger position before the end point is used as the control point.
4. Path. rQuadTo ():
API-provided method preview:
public void rQuadTo(float dx1, float dy1, float dx2, float dy2);
As we mentioned above, the separate r of the method signature is actually the abbreviation of relaytiveLayout. Once we understand this, we will understand that this method is also a method that uses relative positions to customize the View.
Where:
Dx1: The X coordinate of the control point, indicating the displacement coordinate relative to the X coordinate of the last terminal point, positive representing the addition, negative representing the subtraction; dy1: Y coordinate of the control point, it indicates the displacement coordinate relative to the Y coordinate of the last terminal, positive value indicates addition, negative value indicates subtraction, dx2: The X coordinate of the terminal, indicating the displacement value relative to the X coordinate of the last terminal, positive value indicates addition, negative value indicates subtraction; dy2 indicates the Y coordinate of the end point, indicating the displacement value relative to the Y coordinate of the last end point. A positive value indicates addition, and a negative value indicates subtraction;
All the four parameters pass relative values, which are the displacement values relative to the last endpoint.
Public class WaveView extends View {private Paint mPaint; private Path mPath; private Path mPath2; private int mItemWaveLength = 400; // wavelength private int mitemwavelengh2 = 900; // wavelength private int dx = 0; public WaveView (Context context) {super (context); mPaint = new Paint (); mPaint. setStrokeWidth (5); mPaint. setAntiAlias (true); mPaint. setStyle (Paint. style. FILL); mPath = new Path (); mPath2 = new Path () ;}@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); mPath. reset (); mPath2.reset (); int originY = 500; // The distance from the top of the control to int halfWaveLen = mItemWaveLength/2; float Height = 150; // half the height of the wave (the vertical distance between the peak and the valley) // shift the start position of the mPath to the left one wavelength: mPath. moveTo (-mItemWaveLength + dx, originY); mPath2.moveTo (-mitemwavelength1 * 1.15f + dx, originY ); // for loop to draw all the waves that may be stored on the current screen (because the start position of mPath is shifted to the left wavelength, and the right wavelength is also shifted to 2) for (int I =-mItemWaveLength; I <= getWidth () + mItemWaveLength * 2; I ++ = mItemWaveLength) {// draw the first half of a wavelength; halfWaveLen indicates the height of the wave mPath. rQuadTo (halfWaveLen/2, Height/2, halfWaveLen, 0); mPath2.rQuadTo (halfWaveLen/2, Height/2, halfWaveLen, 0 ); // draw the second half of a wavelength, and the depth of the wave. The height is the mPath. rQuadTo (halfWaveLen/2,-Height/2, halfWaveLen, 0); mPath2.rQuadTo (halfWaveLen/2,-Height/2, halfWaveLen, 0);} mPath. lineTo (getWidth (), getHeight (); mPath. lineTo (0, getHeight (); mPath. close (); mPath2.lineTo (getWidth (), getHeight (); mPath2.lineTo (0, getHeight (); mPath2.close (); mPaint. setColor (Color. parseColor ("# 428ddd"); canvas. drawPath (mPath, mPaint); mPaint. setAlpha (20); mPaint. setColor (Color. parseColor ("# 43c5dd"); canvas. drawPath (mPath2, mPaint);} public void startAnim () {ValueAnimator animator = ValueAnimator. ofInt (0, mItemWaveLength); animator. setDuration (500); // animation time animator. setRepeatCount (ValueAnimator. INFINITE); animator. setInterpolator (new LinearInterpolator (); animator. addUpdateListener (new ValueAnimator. animatorUpdateListener () {@ Override public void onAnimationUpdate (ValueAnimator animation) {dx = (int) animation. getAnimatedValue (); postInvalidate () ;}}); animator. start ();}}
Iv. Level 3 besell
The use of the Third-Order besell curve is similar to that of the second-order besell curve. The difference is that two control points are required,
public void cubicTo(float x1,float y1,float x2,float y2,float x3,float y3);public void rCubicTo(float x1,float y1,float x2,float y2,float x3,float y3);
I will discuss this in more detail later, but I will skip it here.