Canvas's translate, scale, rotate, and skew Methods !, Canvasskew

Source: Internet
Author: User
Tags in degrees

Canvas's translate, scale, rotate, and skew Methods !, Canvasskew


Respect Original, welcome to reprint, reprint Please note: FROM GA_studio http://blog.csdn.net/tianjian4592


Previously, Canvas can be divided into three types:

1. save, restore, and other methods related to layer storage and rollback;

2. scale, rotate, clipXXX, and other operations on the canvas;

3. drawXXX and other painting-related methods;


I have discussed the drawBitmap method in the previous section and introduced a planet's floating chestnuts. In this example, the planet is large and small and needs to be moved. Sometimes it may need to be rotated or staggered, with these requirements, we need to use the Canvas-related translate, scale, rotate, and skew methods to translate, zoom, rotate, and stagger, these four words sound so familiar that we often deal with these words when doing some basic animations. Now let's take a look at these guys and Canvas one by one) what are the effects of combined energy;

Of course, two basic concepts must be clarified before reading:

1. the upper left corner of the Canvas is (0, 0 );

2. Based on the upper left corner to the right X is positive, down Y is positive, and vice versa;


1. canvas. translate ()-canvas Translation:

First, draw a 400X400 red rectangle on the canvas.

canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

At this time, a red rectangle appears in the upper left corner of the entire canvas (to make it clearer, place a Blue Base). The rectangle size is 400X400. The effect is as follows:


Next let's play with canvas. translate ().

    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawColor(Color.BLUE);        canvas.translate(100, 100);        canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);    }

See the following results:

You can see that although the same rectangle is drawn, the position of the rectangle on the canvas has shifted by Px to the right and down;

In this case, what will happen if we translate the canvas (translate) (100,100) and draw the same rectangle? Will it overlap with the previous rectangle? We will wait and see:

    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawColor(Color.BLUE);        canvas.translate(100, 100);        canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);        canvas.translate(100, 100);        canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);    }


In terms of effect, the two translate operations are superimposed, And the canvas has been offset (200,200) when the second rectangle is drawn );

Now, let's use canvas. translate () to make a small chestnut and draw a dial commonly used in life;

First, we need to find a dial picture for reference on the Internet:

As shown in the figure, the scale has the following elements: the outer frame, the dial line (the length of different numeric dial lines varies), and numbers.

So all we need to do is to draw the above elements in onDraw respectively:

@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); // draw the outer box drawOuter (canvas); // draw the dial drawLines (canvas); // draw the number drawNumbers (canvas );}
Let's analyze it briefly. The scale has an outer frame, and the outer frame has a certain margin between the left and right. The first and last dial lines have a certain margin from the border, the distance between other dial lines is the same, and the length of some special dial lines is different;

With the above analysis, let's draw the outer frame and the outer frame is a rectangle one by one. You only need to determine the position and size of the border, and then draw it using canvas. drawRect:

Let's first define several required data. For screen adaptation, the data is dp:

// The scale height is private static final int DIVIDING_RULE_HEIGHT = 70; // The distance from the left-right private static final int Limit = 10; // The distance between the first line and the border is private static final int FIRST_LINE_MARGIN = 5; // Number of centimeters to be drawn private static final int DEFAULT_COUNT = 9;
Convert the above data to the corresponding pixel value:

    private void initData() {        mDividRuleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                DIVIDING_RULE_HEIGHT, mResources.getDisplayMetrics());        mHalfRuleHeight = mDividRuleHeight / 2;        mDividRuleLeftMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                DIVIDING_RULE_MARGIN_LEFT_RIGHT, mResources.getDisplayMetrics());        mFirstLineMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                FIRST_LINE_MARGIN, mResources.getDisplayMetrics());    }
With the above data, you can determine that the Rect of the outer border is:

mOutRect = new Rect(mDividRuleLeftMargin, top, mTotalWidth - mDividRuleLeftMargin,                mRuleBottom);

Next, let's look at the drawing of the dial line. We can calculate the number of cells in the center based on the centimeter, and calculate the screen width of each grid based on the screen width and the number of cells occupied by the centimeter:

mLineInterval = (mTotalWidth - 2 * mDividRuleLeftMargin - 2 * mFirstLineMargin)                / (DEFAULT_COUNT * 10 - 1);

With the width of each grid, we only need to constantly shift the right width of the canvas when drawing the dial line:

/*** Draw the dial line * @ param canvas */private void drawLines (Canvas canvas) {canvas. save (); canvas. translate (mLineStartX, 0); int top = mMaxLineTop; for (int I = 0; I <= DEFAULT_COUNT * 10; I ++) {if (I % 10 = 0) {top = mMaxLineTop;} else if (I % 5 = 0) {top = mMiddleLineTop;} else {top = mMinLineTop;} canvas. drawLine (0, mRuleBottom, 0, top, mLinePaint); canvas. translate (mLineInterval, 0);} canvas. restore ();}
Because the scale is divided into three kinds of dial lines of length, we also perform corresponding processing. The dial line of an integer of 10 is the longest, the dial line of an integer of 5 is of medium length, and the rest is short;

The dial size is as follows:


Now, the basic scale is displayed. You can add the corresponding text if you are interested;

As the saying goes, all things go to Rome, except canvas. can you use other methods to implement the translate statement? The answer is yes, of course. for example, you can directly calculate the position of each dial line based on the I value in the for loop during painting, then draw the image directly. In contrast, you can compare the advantages and disadvantages of the two methods. Well, canvas. so much is said in translate;


Ii. canvas. scale ()-zoom of the canvas:

Android provides the following two interfaces for scale:

    /**     * Preconcat the current matrix with the specified scale.     *     * @param sx The amount to scale in X     * @param sy The amount to scale in Y     */    public native void scale(float sx, float sy);    /**     * Preconcat the current matrix with the specified scale.     *     * @param sx The amount to scale in X     * @param sy The amount to scale in Y     * @param px The x-coord for the pivot point (unchanged by the scale)     * @param py The y-coord for the pivot point (unchanged by the scale)     */    public final void scale(float sx, float sy, float px, float py) {        translate(px, py);        scale(sx, sy);        translate(-px, -py);    }
Let's take a look at scale (float sx, float sy). We still use the square above as a chestnut, and call canvas. scale (float sx, float sy) to see the effect;

    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawColor(Color.BLUE);        canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);        canvas.scale(0.5f, 0.5f);        mPaint.setColor(Color.YELLOW);        canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);    }
We scale the canvas to 0.5 times in the x and y directions, and use the Default Benchmark (origin 0, 0). The effect is as follows:


The effect is equivalent to using a nail at (0, 0), and then scaled the x and y of the rectangle to half. Let's look at the second interface scale (float sx, float sy, float px, float py ):

The first two parameters are the multiples of zooming the canvas in the x and y directions, while px and py are the benchmark of zooming, which can be clearly seen from the source code and scale (float sx, float sy) difference:

translate(px, py);scale(sx, sy);translate(-px, -py);
That is, the canvas is first translated to px, py, then scaled, and then scaled back to the original reference point;

We then draw an identical rectangle based on the previous one. x and y both scale to 0.5 times, and the zoom center is the center of the rectangle:

@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); canvas. drawColor (Color. BLUE); canvas. drawRect (new Rect (0, 0,400,400), mPaint); // Save the canvas status canvas. save (); canvas. scale (0.5f, 0.5f); mPaint. setColor (Color. YELLOW); canvas. drawRect (new Rect (0, 0,400,400), mPaint); // roll back the canvas. restore (); canvas. scale (0.5f, 0.5f, 200,200); mPaint. setColor (Color. BLACK); canvas. drawRect (new Rect (0, 0,400,400), mPaint );}

Let's take a look at the following results:


The effect is equivalent to using a nail in the center of the rectangle and then scaling;

Based on the above android implementation, we can actually use the following code to achieve the same effect:

// First translate the canvas to the center canvas of the rectangle. translate (200,200); // scale the canvas. scale (0.5f, 0.5f); // move the canvas back to the original datum point canvas. translate (-200,-200); mPaint. setColor (Color. BLACK); canvas. drawRect (new Rect (0, 0,400,400), mPaint );

So far, we have learned how to scale the canvas. Based on canvas. scale (), let's complete a small example:


The above is a static graph on the network that allows people to produce visual errors. We simulate and draw the above results;

The idea is very simple:

1. draw a square with the same width as the screen;

2. Scale the canvas with the square center as the reference point;

3. Draw the original square During Scaling;

Note: You must use canvas. save () and canvas. restore () to lock and roll back the canvas each time you draw the canvas, so as not to affect the subsequent painting (which will be discussed separately later)

Initialize the paint brush first. Note that the paint brush needs to be set to hollow:

/*** Initialize the Paint brush */private void initPaint () {mPaint = new Paint (Paint. ANTI_ALIAS_FLAG); // set the paint brush to a hollow mPaint. setStyle (Style. STROKE); // set the paint brush color mPaint. setColor (Color. BLACK); // set the width of the paint brush mPaint. setStrokeWidth (mLineWidth );}
Then draw the original square while scaling the canvas cyclically:

/*** Draw a square ** @ param canvas */private void drawSquare (Canvas canvas) {for (int I = 0; I <TOTAL_SQUARE_COUNT; I ++) {// Save the canvas. save (); float fraction = (float) I/TOTAL_SQUARE_COUNT; // scale the canvas in the center of a square. scale (fraction, fraction, mHalfWidth, mHalfHeight); canvas. drawRect (mSquareRect, mPaint); // roll back the canvas. restore ();}}
Let's take a look at the effect:

In fact, the final effect is a little different from that found on the Internet. The smaller the canvas is, the finer the width of the paint brush, and the same width of the original image, but it seems that the effect is better after the width of the paint brush is scaled ......


3. canvas. rotate ()-canvas rotation:

Canvas. rotate () is similar to canvas. scale (). If you understand canvas. scale (), canvas. rotate () is very simple and practical;

To put it simply, canvas. rotate () is to rotate the canvas. Similar to canvas. scale (), it also has two methods to use:

    /**     * Preconcat the current matrix with the specified rotation.     *     * @param degrees The amount to rotate, in degrees     */    public native void rotate(float degrees);    /**     * Preconcat the current matrix with the specified rotation.     *     * @param degrees The amount to rotate, in degrees     * @param px The x-coord for the pivot point (unchanged by the rotation)     * @param py The y-coord for the pivot point (unchanged by the rotation)     */    public final void rotate(float degrees, float px, float py) {        translate(px, py);        rotate(degrees);        translate(-px, -py);    }

The difference between the two methods is that the reference point is selected. By default, the origin point is used as the reference point, and the other is the input x and y as the reference point. Is it exactly the same as scale? Let's turn it together:

Let's first turn the rectangle in the upper left corner, how many degrees? Let's have a 90-degree experience first;

    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawColor(Color.BLUE);        canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);        mPaint.setColor(Color.YELLOW);        canvas.rotate(90);        canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);    }
We expected a cool yellow rectangle to be rotated on the screen. Let's take a look;


What about the yellow rectangle?

Since the datum point is the origin point, We have directly rotated 90 degrees, so we have already rotated the rectangle out of the screen, of course we can't see it, we will reduce the angle to 45 degrees:

    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawColor(Color.BLUE);        canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);        mPaint.setColor(Color.YELLOW);        canvas.rotate(45);        canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);    }

At this point, we can clearly see that the yellow rectangle is the result of 45 degrees after the red rectangle rotates around the origin (0, 0;


Let's change the rotation datum point to the center of the rectangle:

canvas.rotate(45,200,200);

We can see that the yellow rectangle is the result of the red rectangle rotating around the center:


At this point, we have learned about canvas. rotate (float degrees) and canvas. the use of rotate (float degrees, float px, float py) should also be clear about the implementation of the latter as follows:

translate(px, py);rotate(degrees);translate(-px, -py);

Now let's use canvas. rotate () to complete a small example of an alarm dial:

The alarm dial is actually similar to the dial, but the dial is drawn on a straight line, and the dial is drawn on a circumference. In the end, the dial is determined by a specific position;

Since it is a circumference, the simplest way is to draw a line at the 12 o'clock of the alarm, and draw it to the corresponding circumference through the canvas rotation. Let's implement it together:

The whole circumference is 360 degrees, and every 30 degrees is an overall time scale. There are four short scales between the whole scale and the scale, and five small segments are divided, each of which is 6 degrees, with these analyses, we can draw them using the following code:

/*** Draw scale ** @ param canvas */private void drawLines (Canvas canvas) {for (int I = 0; I <= 360; I ++) {if (I % 30 = 0) {mLineBottom = mLineTop + mLongLineHeight; mLinePaint. setStrokeWidth (mLineWidth);} else {mLineBottom = mLineTop + mlineheight; mLinePaint. setStrokeWidth (mHalfLineWidth);} if (I % 6 = 0) {canvas. save (); canvas. rotate (I, mHalfWidth, mHalfHeight); canvas. drawLine (mLineLeft, mLineTop, mLineLeft, mLineBottom, mLinePaint); canvas. restore ();}}}

The effect is as follows:


The overall code is as follows:

/*** Alarm dial ** @ author AJian */public class RotateClockView extends View {private static final int LONG_LINE_HEIGHT = 35; private static final int SHORT_LINE_HEIGHT = 25; private Paint mcircle, Paint, mLinePaint; private DrawFilter mDrawFilter; private int mHalfWidth, mHalfHeight; // ring line width private int mCircleLineWidth, mHalfCircleLineWidth; // linear dial line width private int mLineWidth, mHalfLineWidth; // long line length private int mLongLineHeight; // short line length private int mShortLineHeight; // The left and top positions of the dial line private int mLineLeft and mLineTop; // The bottom position of the dial line private int mLineBottom; // used to control the dial line position private int mFixLineHeight; public RotateClockView (Context context) {super (context); mDrawFilter = new PaintFlagsDrawFilter (0, Paint. ANTI_ALIAS_FLAG | Paint. FILTER_BITMAP_FLAG); mCircleLineWidth = (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, 8, getResources (). getDisplayMetrics (); mHalfCircleLineWidth = mCircleLineWidth; mLineWidth = (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, 4, getResources (). getDisplayMetrics (); mHalfLineWidth = mLineWidth/2; mFixLineHeight = (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, 4, getResources (). getDisplayMetrics (); mLongLineHeight = (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, LONG_LINE_HEIGHT, getResources (). getDisplayMetrics (); m1_lineheight = (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, SHORT_LINE_HEIGHT, getResources (). getDisplayMetrics (); initPaint ();} private void initPaint () {mCirclePaint = new Paint (Paint. ANTI_ALIAS_FLAG); mCirclePaint. setColor (Color. RED); // set the paint brush to a hollow mCirclePaint. setStyle (Style. STROKE); // set the paint width mCirclePaint. setStrokeWidth (mCircleLineWidth); mLinePaint = new Paint (Paint. ANTI_ALIAS_FLAG); mLinePaint. setColor (Color. RED); mLinePaint. setStyle (Style. FILL_AND_STROKE); // set the paint width to mLinePaint. setStrokeWidth (mLineWidth);} @ Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {super. onMeasure (widthMeasureSpec, heightMeasureSpec);} @ Override protected void onDraw (Canvas canvas) {canvas. setDrawFilter (mDrawFilter); super. onDraw (canvas); // draw the dial drawCircle (canvas); // draw the scale drawLines (canvas );} /*** draw scale ** @ param canvas */private void drawLines (Canvas canvas) {for (int I = 0; I <= 360; I ++) {if (I % 30 = 0) {mLineBottom = mLineTop + mLongLineHeight; mLinePaint. setStrokeWidth (mLineWidth);} else {mLineBottom = mLineTop + mlineheight; mLinePaint. setStrokeWidth (mHalfLineWidth);} if (I % 6 = 0) {canvas. save (); canvas. rotate (I, mHalfWidth, mHalfHeight); canvas. drawLine (mLineLeft, mLineTop, mLineLeft, mLineBottom, mLinePaint); canvas. restore () ;}}/ *** draw dial ** @ param canvas */private void drawCircle (Canvas canvas) {canvas. drawCircle (mHalfWidth, mHalfHeight, mHalfWidth-mHalfCircleLineWidth, mCirclePaint);} @ Override protected void onSizeChanged (int w, int h, int oldw, int oldh) {super. onSizeChanged (w, h, oldw, oldh); mHalfWidth = w/2; mHalfHeight = h/2; mLineLeft = mHalfWidth-weight; mLineTop = mHalfHeight-mHalfWidth + mFixLineHeight ;}}

Similarly, if you are interested, you can add text by yourself;


4. canvas. skew ()-Incorrect canvas cut:

    /**     * Preconcat the current matrix with the specified skew.     *     * @param sx The amount to skew in X     * @param sy The amount to skew in Y     */    public native void skew(float sx, float sy);

This method only needs to understand two parameters:

Float sx: Tilt the canvas to the corresponding angle in the x direction. sx is the tan value of the tilt angle;

Float sy: Tilt the canvas to the corresponding angle on the Y axis. sy is the tan value of the tilt angle;

Note: here all the tan values of the skew angle, for example, we intend to tilt 45 degrees on the X axis, tan45 = 1;

First, tilt the X axis to 45 degrees. Let's take a look:

@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); canvas. drawColor (Color. BLUE); canvas. drawRect (new Rect (0, 0,400,400), mPaint); // The canvas is tilted 45 degrees in the x direction. skew (1, 0); mPaint. setColor (0x8800ff00); canvas. drawRect (new Rect (0, 0,400,400), mPaint );}
The effect is as follows:


Tilt the Y axis 45 degrees to see:

@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); canvas. drawColor (Color. BLUE); canvas. drawRect (new Rect (0, 0,400,400), mPaint); // tilt 45 degrees canvas to y. skew (0, 1); mPaint. setColor (0x8800ff00); canvas. drawRect (new Rect (0, 0,400,400), mPaint );}
The effect is as follows:



There is so much to say about the translate, scale, rotate, and skew of the Canvas, which is not complex, flexible use often solves many seemingly complex problems in plotting. Therefore, it focuses on understanding and timely and appropriate association when you see relevant effects.


Of course, operations on the Canvas often use the Matrix (which will be discussed separately later) to achieve the same effect. For more information, see a brilliant loading animation analysis and implementation!


Source code download link




Related Article

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.