preface

Long ago began to see the article on the custom View, this two days of practice before to see the knowledge of the custom View, draw a control. Although this control looks simple, the basic usage of custom View is used. The sparrow may be small, but it has all the organs, so let’s record the process.

About custom View article, I recommend GcsSloop in this wall split android custom View tutorial. This is the article I think is better written about the custom View, and is very complete is also very detailed, easy to understand.

So, back to the subject, let’s start by drawing a circle that isn’t simple. Let’s look at the renderings. I know you’re not going to look down without the renderings.



GIF effect has a bit of a slow, real machine is very smooth.

The implementation process

Get a sketch and see how to draw it first. Think it over before you go to work. Sometimes you get twice the result with half the effort. . Our effect is very simple, all we need now is to draw a circle and then write a word on it.

Draw a circle

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    final int paddingLeft = getPaddingLeft();
    final int paddingRight = getPaddingRight();
    final int paddingTop = getPaddingTop();
    final int paddingBottom = getPaddingBottom();
    int width = getWidth() - paddingLeft - paddingRight;
    int height = getHeight() - paddingTop - paddingBottom;
    float centerX = paddingLeft + width / 2;
    float centerY = paddingTop + height / 2;
    float radius = Math.min(width, height) / 2;
    canvas.drawCircle(centerX, centerY, radius, mCirclePaint);
}Copy the code

The code to draw the circle is very simple, because it inherits from the View, so the padding needs to be handled in the onDrow method first, otherwise the padding will be invalid. After getting the actual width and height of our View, we will get the center of the View as the center of the circle. Since the origin of my canvas’s coordinates is in the upper left corner of the screen, I need to add the distance between paddingLeft and paddingTop to get the center point. The point of centerX and centerY is the central point of our layout. After taking the center point, use half of the minimum length and width as the radius, and then you can draw the circle. So easy!

Write a word

DrawText is a four-argument method. The second and third arguments are not the center of the text. They are the X value to the left and the Y value to the bottom of the text. Take a look at the source code to know:

/** * Draw the text, with origin at (x,y), using the specified paint. The * origin is interpreted based on the Align setting in the paint. * * @param text The text  to be drawn * @param x The x-coordinate of the origin of the text being drawn * @param y The y-coordinate of the baseline of the text being drawn * @param paint The paint used for the text (e.g. color, size, style) */ public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); }Copy the code

So what is baseline? Baseline is the Y value at the bottom of the text. Take a look at the chart below to see why:

To place the text in the center of the circle, find the baseline and the left X value of the text.

private void drawVerticalText(Canvas canvas, float centerX, float centerY, String text) {
    Paint.FontMetrics fontMetrics = mVerticalTextPaint.getFontMetrics();
    float baseLine = -(fontMetrics.ascent + fontMetrics.descent) / 2;
    float textWidth = mVerticalTextPaint.measureText(text);
    float startX = centerX - textWidth / 2;
    float endY = centerY + baseLine;
    canvas.drawText(text, startX, endY, mVerticalTextPaint);
}Copy the code

There are a lot of articles on the web about drawText, but most of them are wrapped around a Rect, so I’m going to draw it directly. Here I encapsulate the drawing as a method that takes the FontMetrics object from the brush and uses it to get the properties of the text, including Ascent and Descent values. Then calculate the baseline, using Paint’s measureText method to get the length of the text. Finally, put the text in the middle according to the center offset, OK. Take a look at the results:

Text landscape

Now, I have another requirement, I want the text to be vertical how do I do that? The first thing that comes to mind is should drawText have a method or argument that can control the direction? However, it is not so easy. DrawText doesn’t drawText vertically, so what about drawTextOnPath? I’m going to put a path in the vertical direction of the circle, but it’s not going to be very good, so I’m not going to show you the graph. The last thing I came up with was a class called StaticLayout, which uses line breaks for vertical formatting, which is actually used in the TextView source code. Look at the main code:

. mHorizontalTextPaint = new TextPaint(mVerticalTextPaint); MStaticLayout = new StaticLayout(mText, mHorizontalTextPaint, 1, layout.align_Center, 1.0f, 0.0f, false); mStaticLayout = new StaticLayout(mText, mHorizontalTextPaint, 1, Layout.align_Center, 1.0f, 0.0f, false); . private void drawHorizontalText(Canvas canvas, float centerX, float centerY) { canvas.translate(centerX, centerY - mStaticLayout.getHeight() / 2); mStaticLayout.draw(canvas); }Copy the code

The StaticLayout constructor needs to pass TextPaint as the second parameter. TextPaint inherits from Paint, so we can pass the same Paint object as the horizontal text we drew above, without resetting the color, font size and other properties. I won’t go into detail about what the arguments to the StaticLayout constructor do. We’ll focus on the third argument. This parameter means to wrap as many characters as possible. Here we pass a 1 to wrap one. Then move the X-axis of the canvas to the center of the circle, and half the height of the text is the value of the Y-axis of the circle. In this way, we have vertical text:

Arbitrary Angle of text

Continue to add requirements, requirements for text 45° oblique display how to do? This simple, canvas coordinate rotation can not be ~

canvas.rotate(mAngle, centerX, centerY);
drawText(canvas, centerX, centerY);
Copy the code

Just call the rotate method of the canvas, one line of code. But the important thing here is to rotate the canvas before drawText, and then drawText. Be sure to rotate the canvas before drawText! Otherwise, there is no effect, I jumped into the pit here. T.T rotated the canvas after drawText, no reaction. I thought there was something wrong.

rotating

Static text avatar is a little monotonous, then add an animation. All right, let’s add it. Let’s add a rotating one.

public void startRotateAnimation() { if (mAnimator == null) { initRotateAnimation(); } if (! mAnimator.isRunning()) { mAnimator.start(); } } private void initRotateAnimation() { if (mRotateOrientation == ROTATE_CLOCKWISE) { mRotateValues = 360f; } else if (mRotateOrientation == ROTATE_ANTICLOCKWISE) { mRotateValues = -360f; } mAnimator = ValueAnimator.ofFloat(0f, mRotateValues); mAnimator.setInterpolator(new LinearInterpolator()); mAnimator.setDuration(mSpeed); mAnimator.addUpdateListener(this); mAnimator.setRepeatCount(ValueAnimator.INFINITE); mAnimator.setRepeatMode(ValueAnimator.RESTART); } @Override public void onAnimationUpdate(ValueAnimator animation) { mAngle = (float) animation.getAnimatedValue(); invalidate(); } public void stopRotateAnimation() { if (mAnimator ! = null && mAnimator.isRunning()) { mAnimator.cancel(); }}Copy the code

These are the four main ways to animate rotation. ValueAnimator dynamically and linearly change a floating point value from 0 to 360. Use the AnimatorUpdateListener onAnimationUpdate method to get a range of values. MAngle is the value of any of the above Angle text and dynamically change this value. Constantly redraw the interface, you can achieve text rotation animation. The 360F and -360F on the top are used to control the direction, clockwise or counterclockwise rotation. Speed is controlled by setDuration. Turn up:

The zoom

See that FloatingActionButton comes with show and hide animations, so I have a similar animation here.

public void show() { if (! IsShow) {scaleAnimation(0.0f, 1.0f, 300); isShow = true; }} public void hide() {if (isShow) {scaleAnimation(1.0f, 0.0f, 300); isShow = false; } } private void scaleAnimation(float start, float end, long duration) { ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "scaleX", start, end); ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "scaleY", start, end); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.setDuration(duration); animatorSet.playTogether(animatorX, animatorY); animatorSet.start(); }Copy the code

ScaleX direction “scaleX” and Y direction “scaleY” again using the property ObjectAnimator. Put two animations into a single AnimatorSet and execute them simultaneously, showing from 0f to 1f and hiding from 1F to 0f.

conclusion

At this point, the text head control on the control is finished. In fact, there is nothing very difficult, just to define the basic process of View wrote again. For learning to customize a View, it is not enough to just look at it, we must practice it, and it is not as difficult as we imagined.

In the meantime, I’ve wrapped up the CircleView and published it to JCenter so I don’t have to duplicate the wheel.

Project address: github.com/backkomyoun…

Click a Star if you like!