About this article: this article was originally published in my CSDN blog (by the image watermark can be found), sorting out the past blog process, found that the summary was very careful, so it will be migrated here, I hope to help you in the custom View, 💗
The introduction
Android custom View application is very extensive, recently visited Github is accidentally found a Demo feeling very well written, COMBINED with the content of this project, I tell you how to draw the clock dial, also can deepen their understanding of the custom View, involves a lot of content, we slowly absorb.
Final effect:
Before we start, let me show you the final result
now
So let’s build this View first
- First, we define a custom View called ClockView that inherits from the View class.
- Then, in the /res/values directory, create the attrs file and define some attributes as follows
<?xml version="1.0" encoding="utf-8"? >
<resources>
<declare-styleable name="ClockView">
<attr name="clock_backgroundColor" format="color" />
<attr name="clock_lightColor" format="color" />
<attr name="clock_darkColor" format="color" />
<attr name="clock_textSize" format="dimension" />
</declare-styleable>
</resources>
Copy the code
Preparation for drawing the outer hour ring
The hour circle consists of an outer circle and a four-hour number, so what we need is clear.
- We first need a Paint object to draw text,
- You also need another Paint object to draw the circle.
Override constructor:
/* Dark color, arc, scale line, hour, gradient start color */
private int mDarkColor;
/* Hour text font size */
private float mTextSize;
private Paint mTextPaint;
private Paint mCirclePaint;
public ClockView(Context context) {
super(context);
}
public ClockView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ClockView, 0.0);
mDarkColor = ta.getColor(R.styleable.ClockView_clock_darkColor, Color.parseColor("#80ffffff"));
mTextSize = ta.getDimension(R.styleable.ClockView_clock_textSize, DensityUtils.sp2px(context, 14));
ta.recycle();
// ANTI_ALIAS_FLAG draws smoothly without stumbling
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setColor(mDarkColor);
// Center the text
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setTextSize(mTextSize);
mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setColor(mDarkColor);
// Official: Geometry and text drawn with this style will be stroked, respecting the fields related to strokes on the drawing.
// In plain English, don't paint the whole sector, just stroke the outer edge
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setStrokeWidth(mCircleStrokeWidth);// Stroke width
}
Copy the code
Don’t forget to rewrite the onMeasure method to measure the size of a MeasureSpec. Refer to the Custom View article for details on how to measure the size of a MeasureSpec.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getMeasureResult(widthMeasureSpec), getMeasureResult(heightMeasureSpec));
}
private int getMeasureResult(int measureSpec){
int defaultSize = 800;
int size = MeasureSpec.getSize(measureSpec);
int mode = MeasureSpec.getMode(measureSpec);
switch (mode){
case MeasureSpec.UNSPECIFIED:
return defaultSize;
case MeasureSpec.AT_MOST:
return Math.max(defaultSize, size);
case MeasureSpec.EXACTLY:
return size;
default:
returndefaultSize; }}Copy the code
Start drawing the outer ring
As we know, for drawing circles and ellipses, we often need to set a boundary rectangle with RectF before drawing. Rect if you’re drawing text.
So to draw the outer ring, first define a RectF variable to draw the ring, and then define a Rect variable to draw the text.
Note that the mCanvas drawing class is an argument in onDraw, and we save it in onDraw
// Measure text size
private Rect mTextRect = new Rect();
private RectF mCircleRectF = new RectF();
/* Hour circle line width */
private float mCircleStrokeWidth = 4;
/** * Draw the outer circle of time 12, 3, 6, 9 text and 4 arcs */
private void drawOutSideArc(a) {
String[] timeList = new String[]{"12"."3"."6"."9"};
// Calculate the height of the number
mTextPaint.getTextBounds(timeList[0].0, timeList[0].length(), mTextRect);// Return a rectangle to mTextRect.
mCircleRectF.set(mTextRect.width() / 2 + mCircleStrokeWidth / 2.// Draw a small outer rectangle and draw a circle inside the rectangle
mTextRect.height() / 2 + mCircleStrokeWidth / 2,
getWidth() - mTextRect.width() / 2 - mCircleStrokeWidth / 2,
getHeight() - mTextRect.height() / 2 - mCircleStrokeWidth / 2);
mCanvas.drawText(timeList[0], getWidth() / 2, mCircleRectF.top + mTextRect.height() / 2, mTextPaint);// Get the boundary values by RectF, because the vertices are written in the upper right, so shift down
mCanvas.drawText(timeList[1], mCircleRectF.right, getHeight() / 2 + mTextRect.height() / 2, mTextPaint);
mCanvas.drawText(timeList[2], getWidth() / 2, mCircleRectF.bottom + mTextRect.height() / 2, mTextPaint);
mCanvas.drawText(timeList[3], mCircleRectF.left, getHeight() / 2 + mTextRect.height() / 2, mTextPaint);
// Draw 4 arcs connecting numbers
for (int i = 0; i < 4; i++) {
// Draw four arcs
mCanvas.drawArc(mCircleRectF, 5 + 90 * i, 80.false, mCirclePaint); }}Copy the code
Next, we override the onDraw() method and, within the onDraw() method, call the above method to draw the circle
private Canvas mCanvas;
@Override
protected void onDraw(Canvas canvas) {
mCanvas = canvas;
drawOutSideArc();
}
Copy the code
Let’s run it and see what it looks like
In the drawing process, the McIrectf object that controls our circle is bounded by the entire control size, so the reason is clear. So we just set the mCircleRectF object to be a square.
Overrides the onSizeChanged() method to make sure it draws a circle
Package positive drawing is circular if:
- Make sure RectF cuts a square
- So to ensure that RextF is a square, we need to know the distance between the four sides of the square and the control boundary
- That is we need to calculate four integer variables: 1. MPaddingLeft | 2. MPaddingTop | 3. MPaddingRight | 4. MPaddingBottom
private float mRadius;
/* Add a default padding value to prevent camera clock rotation from exceeding the view size */
private float mDefaultPadding;
private float mPaddingLeft;
private float mPaddingTop;
private float mPaddingRight;
private float mPaddingBottom;// The above 4 values are measured in onSizechanged()
@Override
protected void onSizeChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mRadius = Math.min(l - getPaddingLeft() - getPaddingRight(),
t - getPaddingTop() - getPaddingBottom()) / 2;// The length of each pointer
mDefaultPadding = 0.12 f * mRadius;
mPaddingLeft = mDefaultPadding + l / 2 - mRadius + getPaddingLeft();// How far is the clock from the left boundary
mPaddingRight = mDefaultPadding + l / 2 - mRadius + getPaddingRight();// How far is the clock from the right boundary
mPaddingTop = mDefaultPadding + t / 2 - mRadius + getPaddingTop();// How far is the clock from the upper boundary
mPaddingBottom = mDefaultPadding + t / 2 - mRadius + getPaddingBottom();// How far is the clock from the bottom boundary
}
Copy the code
For the radius of the circle, mRadius, we’re going to take half of the length and width of the control, and then there’s another case where if the control has padding then if the knowledge has padding, whatever the padding is, The radius of the control is always half of the width of the control. This makes the padding out of the control and makes it look less human, so the real radius should be the width of the control, minus two padding values, as follows:
mRadius = Math.min(w – getPaddingLeft() – getPaddingRight(), h – getPaddingTop() – getPaddingBottom()) / 2;
So what does mDefaultPadding do? How about we put it in the mountains to see the effect:
Preparation for drawing scale lines
Before we start drawing, we need to prepare a few tools,
- First, a Paint object is necessary,
- And then for the user’s convenience, we define a color, expose the Settings,
- Finally we need an int to set the length of the scale
/* The scale line length */
private float mScaleLength;
/* Scale brush */
private Paint mScaleLinePaint;
/* Background color */
private int mBackgroundColor;
public ClockView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ClockView, 0.0);
mBackgroundColor = ta.getColor(R.styleable.ClockView_clock_backgroundColor, Color.parseColor("#237EAD"));
mDarkColor = ta.getColor(R.styleable.ClockView_clock_darkColor, Color.parseColor("#80ffffff"));
mTextSize = ta.getDimension(R.styleable.ClockView_clock_textSize, DensityUtils.sp2px(context, 14));
ta.recycle();
.
.
.
mScaleLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mScaleLinePaint.setStyle(Paint.Style.STROKE);
mScaleLinePaint.setColor(mBackgroundColor);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
.
.
.
mScaleLength = 0.12 f * mRadius;// Determine the scale length according to the scale
mScaleLinePaint.setStrokeWidth(0.012 f * mRadius);// The width of the scale circle
}
Copy the code
Start drawing scale lines
On the contrary, it is very simple to draw the country morning. For us, it is best to divide it into 360 parts in 60 minutes and 60 minutes. However, due to the small screen of the mobile phone, it will be too dense at first and become round:
So here, we divide 360 degrees into 200 pieces,
- 360/200 = 1.8 f
- When drawing, we rotated the Canvas Angle by 1.8F without drawing an edge
- Starting point: Each time we start at the top of the artboard, move a Padding down and add the height of the mTextRect, which is the height of the oclock text, and then add the length of the scale line to separate the scale lines from the arc so they don’t stick together
- End point: one more scale line is required at the starting point of the pen
/** * Draw a gradient rendered light and dark gradient arc, rotate as you redraw, and cover it with a scale line of background color */
private void drawScaleLine(a) {
mCanvas.save();
// Draw the background color scale line
for (int i = 0; i < 100; i++) {
mCanvas.drawLine(getWidth() / 2, mPaddingTop + mScaleLength + mTextRect.height() / 2,
getWidth() / 2, mPaddingTop + 2 * mScaleLength + mTextRect.height() / 2, mScaleLinePaint);
mCanvas.rotate(1.8 f, getWidth() / 2, getHeight() / 2);
}
mCanvas.restore();
}
Copy the code
You’re done
Project Demo address: github.com/FishInWater…
If you have any mistakes, please point them out in the comments section. Thank you very much
Have fun programming!