I want to do something at home during the weekend. I remember the interview more than a year ago, the company directly sent me an interview question, which asked me to make a simple dial effect, but I didn’t make it at that time, so I didn’t feel shy to go to the interview. Today, I will realize it, and it can be divided into the following steps
Step 1: Draw a simple circle
Step 2: Draw the scale
Step 3: Draw time, minutes, table pointer
Step 4: Draw the current time text
Step 5: Time dynamic display
The first step to draw a circle is easy,
package com.example.clockview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.ImageView;
/ * * * Created by Adminis on 2016/11/6.
* /public class ClockView extends ImageView {
private static final String TAG = "ClockView";
private Paint mPaint;
private int widhth = 200;// Width of the control private int height = 200;// Height of the control private int padding = 5;
public ClockView(Context context) {
this(context, null);
}
public ClockView(Context context.AttributeSet attrs) {
this(context.attrs.0);
}
public ClockView(Context context.AttributeSet attrs, int defStyleAttr) {
super(context.attrs.defStyleAttr);
initPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widhth.height);
}
private void initPaint() {
mPaint = new Paint();
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("# 666666"));
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
drawCircle(canvas);
}
/ * ** draw round * @param canvas
* / private void drawCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(widhth/2.height/2.widhth/2-padding.mPaint);
}}Copy the code
Effect:
Step 2: Draw the scale
/ * ** Draw scale * @param canvas
* /private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
canvas.drawLine(widhth / 2 - padding.padding.widhth / 2 - padding.padding + 4 + 8.mPaint);
}Copy the code
Effect:
But we’re going to draw something like this:
I have to draw 12 of these lines together, so that’s the same thing as the Angle between every two lines is 360/12 is equal to 30 degrees
Analysis as shown in figure:
Rotate the canvas 30 degrees after each thread
/ * ** Draw scale * @param canvas
* /private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
for(int i=0;i<12;i++){
if(i%3= =0) {// The corresponding line length points are 12, 3, 6, 9 canvas.drawLine(widhth / 2 - padding.padding.widhth / 2 - padding.padding + 4 + 15.mPaint);
}else{
canvas.drawLine(widhth / 2 - padding.padding.widhth / 2 - padding.padding + 4 + 8.mPaint);
}
canvas.rotate(30.widhth / 2.widhth / 2);
}}Copy the code
Effect:
The scale is drawn using the canvas’s rotate() method, but the center of the circle is the rotation point
The third step is to draw the hands of the time table
The drawing points to the current time,
/ * ** Draw the timing table pointer * @param canvas
* /private void drawPointer(Canvas canvas) {
mCalendar = Calendar.getInstance(a);
mHour = mCalendar.get(Calendar.HOUR);
mMinuate = mCalendar.get(Calendar.MINUTE);
mSecond = mCalendar.get(Calendar.SECOND);
// Rotation per hour mDegrees = mHour*30+mMinuate/2;
mPaint.setColor(Color.BLACK);
canvas.save();
canvas.rotate(mDegrees.widhth / 2.widhth / 2);
canvas.drawLine(widhth / 2.height / 2.widhth / 2.widhth / 2 - mHourLineLen.mPaint);
canvas.restore();
/ / minute mPaint.setColor(Color.RED);
mDegrees = mMinuate*6+mSecond/10;
canvas.save();
canvas.rotate(mDegrees.widhth / 2.widhth / 2);
canvas.drawLine(widhth / 2.height / 2.widhth / 2.widhth / 2 - mHourLineLen.mPaint);
canvas.restore();
// Draw the needle mPaint.setColor(Color.BLUE);
mDegrees = mSecond*6;
canvas.save();
canvas.rotate(mDegrees.widhth / 2.widhth / 2);
canvas.drawLine(widhth / 2.height / 2.widhth / 2.widhth / 2 - mHourLineLen.mPaint);
canvas.restore();
}Copy the code
Effect:
Let me explain the value of this mDegrees for example, it’s 21:20 now, but in a 12-hour system it’s 9:20, which is 9*30 degrees =270 degrees. But the hour hand pointing at 9 is definitely not correct, because there are still 20 minutes left. The needle and thread must be pointing between 9 and 10, so the hour is 30 degrees, let’s take 21:30, and the Angle of the hour would be 30*9+30/2=285 degrees which means 60 minutes 1 hour and 1 hour is 30 degrees, That means that every two minutes the hour hand should move, and according to this principle the calculation between minutes and watches is similar,
Another special point to note is that 2 is the hour hand after drawing, which is based on the current hour hand number, such as 9, and then rotated 30*9. However, the canvas uses animation and must be restored to the original one after drawing, otherwise there will be a problem when you draw for a minute. I have encountered this problem
Special changes have been made: class code copy to see the effect:
package com.example.clockview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.ImageView;
import java.util.Calendar;
/ * * * Created by Adminis on 2016/11/6.
* /public class ClockView extends ImageView {
private static final String TAG = "ClockView";
private Paint mPaint;
private int widhth = 200;// Width of the control private int height = 200;// Height of the control private int padding = 5;
private Calendar mCalendar;
private int mHour;/ / hour private int mMinuate;/ / minute private int mSecond;/ / SEC. private float mDegrees ;// Since the circle is 360 degrees we have 12 scales so it is 360/12 private int mHourLineLen;// time pointer line private int mMinuateLine;Line / / minute private int mSecondLine ;/ / table clock line public ClockView(Context context) {
this(context, null);
}
public ClockView(Context context.AttributeSet attrs) {
this(context.attrs.0);
}
public ClockView(Context context.AttributeSet attrs, int defStyleAttr) {
super(context.attrs.defStyleAttr);
initPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widhth.height);
mHourLineLen = (int) (widhth/2*0.6);
mMinuateLine = (int) (widhth/2*0.7);
mSecondLine = (int) (widhth/2*0.8);
}
private void initPaint() {
mPaint = new Paint();
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("# 666666"));
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
drawCircle(canvas);
drawScale(canvas);
canvasCenterCircle(canvas);
drawPointer(canvas);
}
/ * ** Draw a point in the center of the circle * @param canvas
* / private void canvasCenterCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(widhth / 2.height / 2.5.mPaint);
}
/ * ** draw round * @param canvas
* / private void drawCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(widhth / 2.height / 2.widhth / 2 - padding.mPaint);
}
/ * ** Draw scale * @param canvas
* / private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
for(int i=0;i<12;i++){
if(i%3= =0) {// The corresponding line length points are 12, 3, 6, 9 canvas.drawLine(widhth / 2 - padding.padding.widhth / 2 - padding.padding + 4 + 15.mPaint);
}else{
canvas.drawLine(widhth / 2 - padding.padding.widhth / 2 - padding.padding + 4 + 8.mPaint);
}
canvas.rotate(30.widhth / 2.widhth / 2);
}}/ * ** Draw the timing table pointer * @param canvas
* / private void drawPointer(Canvas canvas) {
mCalendar = Calendar.getInstance(a);
mHour = mCalendar.get(Calendar.HOUR);
mMinuate = mCalendar.get(Calendar.MINUTE);
mSecond = mCalendar.get(Calendar.SECOND);
// Rotation per hour mDegrees = mHour*30+mMinuate/2;
mPaint.setColor(Color.BLACK);
canvas.save();
canvas.rotate(mDegrees.widhth / 2.widhth / 2);
canvas.drawLine(widhth / 2.height / 2.widhth / 2.widhth / 2 - mHourLineLen.mPaint);
canvas.restore();
/ / minute mPaint.setColor(Color.parseColor("# 666666"));
mPaint.setStrokeWidth(5);
mDegrees = mMinuate*6+mSecond/10;
canvas.save();
canvas.rotate(mDegrees.widhth / 2.widhth / 2);
canvas.drawLine(widhth / 2.height / 2.widhth / 2.widhth / 2 - mMinuateLine.mPaint);
canvas.restore();
// Draw the needle mPaint.setStrokeWidth(2);
mPaint.setColor(Color.parseColor("# 666666"));
mDegrees = mSecond*6;
canvas.save();
canvas.rotate(mDegrees.widhth / 2.widhth / 2);
canvas.drawLine(widhth / 2.height / 2.widhth / 2.widhth / 2 - mSecondLine.mPaint);
canvas.restore();
}}Copy the code
Effect:
The fourth step is to draw the current time text below the center point:
/ * ** Draw text * @param canvas
* /private void drawStr(Canvas canvas) {
mPaint.setTextSize(24);
StringBuffer sb = new StringBuffer();
if(mHour<10){
sb.append("0").append(String.valueOf(mHour)).append(":");
}else{
sb.append(String.valueOf(mHour)).append(":");
}
if(mMinuate<10){
sb.append("0").append(String.valueOf(mMinuate)).append(":");
}else{
sb.append(String.valueOf(mMinuate)).append(":");
}
if(mSecond<10){
sb.append("0").append(String.valueOf(mSecond));
}else{
sb.append(String.valueOf(mSecond));
}
String str = sb.toString();
int strW = (int) mPaint.measureText(str);
canvas.drawText(str.widhth / 2 - strW / 2.widhth / 2 + 30.mPaint);
}Copy the code
Effect:
The final step is to use the Handler to refresh the interface every second to dynamically display the time:
The final complete code:
package com.example.clockview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.widget.ImageView;
import java.util.Calendar;
/ * * * Created by Adminis on 2016/11/6.
* /public class ClockView extends ImageView {
private static final String TAG = "ClockView";
private Paint mPaint;
private int widhth = 200;// Width of the control private int height = 200;// Height of the control private int padding = 5;
private Calendar mCalendar;
private int mHour;/ / hour private int mMinuate;/ / minute private int mSecond;/ / SEC. private float mDegrees ;// Since the circle is 360 degrees we have 12 scales so it is 360/12 private int mHourLineLen;// time pointer line private int mMinuateLine;Line / / minute private int mSecondLine ;/ / table clock line private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
invalidate();
}};
public ClockView(Context context) {
this(context, null);
}
public ClockView(Context context.AttributeSet attrs) {
this(context.attrs.0);
}
public ClockView(Context context.AttributeSet attrs, int defStyleAttr) {
super(context.attrs.defStyleAttr);
initPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widhth.height);
mHourLineLen = (int) (widhth/2*0.6);
mMinuateLine = (int) (widhth/2*0.7);
mSecondLine = (int) (widhth/2*0.8);
}
private void initPaint() {
mPaint = new Paint();
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("# 666666"));
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
drawCircle(canvas);
drawScale(canvas);
canvasCenterCircle(canvas);
drawPointer(canvas);
drawStr(canvas);
mHandler.sendEmptyMessage(1);
}
/ * ** Draw text * @param canvas
* / private void drawStr(Canvas canvas) {
mPaint.setTextSize(24);
StringBuffer sb = new StringBuffer();
if(mHour<10){
sb.append("0").append(String.valueOf(mHour)).append(":");
}else{
sb.append(String.valueOf(mHour)).append(":");
}
if(mMinuate<10){
sb.append("0").append(String.valueOf(mMinuate)).append(":");
}else{
sb.append(String.valueOf(mMinuate)).append(":");
}
if(mSecond<10){
sb.append("0").append(String.valueOf(mSecond));
}else{
sb.append(String.valueOf(mSecond));
}
String str = sb.toString();
int strW = (int) mPaint.measureText(str);
canvas.drawText(str.widhth / 2 - strW / 2.widhth / 2 + 30.mPaint);
}
/ * ** Draw a point in the center of the circle * @param canvas
* / private void canvasCenterCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(widhth / 2.height / 2.5.mPaint);
}
/ * ** draw round * @param canvas
* / private void drawCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(widhth / 2.height / 2.widhth / 2 - padding.mPaint);
}
/ * ** Draw scale * @param canvas
* / private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
for(int i=0;i<12;i++) {
if (i % 3 = =0) {// The corresponding line length points are 12, 3, 6, 9 canvas.drawLine(widhth / 2 - padding.padding.widhth / 2 - padding.padding + 4 + 15.mPaint);
}else{
canvas.drawLine(widhth / 2 - padding.padding.widhth / 2 - padding.padding + 4 + 8.mPaint);
}
canvas.rotate(30.widhth / 2.widhth / 2);
}}/ * ** Draw the timing table pointer * @param canvas
* / private void drawPointer(Canvas canvas) {
mCalendar = Calendar.getInstance(a);
mHour = mCalendar.get(Calendar.HOUR);
mMinuate = mCalendar.get(Calendar.MINUTE);
mSecond = mCalendar.get(Calendar.SECOND);
// Rotation per hour mDegrees = mHour*30+mMinuate/2;
mPaint.setColor(Color.BLACK);
canvas.save();
canvas.rotate(mDegrees.widhth / 2.widhth / 2);
canvas.drawLine(widhth / 2.height / 2.widhth / 2.widhth / 2 - mHourLineLen.mPaint);
canvas.restore();
/ / minute mPaint.setColor(Color.parseColor("# 666666"));
mPaint.setStrokeWidth(5);
mDegrees = mMinuate*6+mSecond/10;
canvas.save();
canvas.rotate(mDegrees.widhth / 2.widhth / 2);
canvas.drawLine(widhth / 2.height / 2.widhth / 2.widhth / 2 - mMinuateLine.mPaint);
canvas.restore();
// Draw the needle mPaint.setStrokeWidth(2);
mPaint.setColor(Color.parseColor("# 666666"));
mDegrees = mSecond*6;
canvas.save();
canvas.rotate(mDegrees.widhth / 2.widhth / 2);
canvas.drawLine(widhth / 2.height / 2.widhth / 2.widhth / 2 - mSecondLine.mPaint);
canvas.restore();
}}Copy the code
Dynamic effect:
Finally finished!
Someone in the group just said that the hour hand and the needle line of the watch at 3 o ‘clock and 15 seconds did not coincide, so I wrote a data by myself and tested it and found that there was really this bug. The reason for this bug is as follows
It’s the X-axis that starts and ends in the wrong position, so I should get rid of the -padding
/ * ** Draw scale * @param canvas * /private void drawScale(Canvas canvas) { mPaint.setStyle(Paint.Style.FILL); for(int i=0;i<12;i++) { if (i % 3 = =0) {// The corresponding line length points are 12, 3, 6, 9 canvas.drawLine(widhth / 2-padding .padding.widhth / 2 -padding.padding + 4 + 15.mPaint); }else{ canvas.drawLine(widhth / 2-padding .padding.widhth / 2-padding.padding + 4 + 8.mPaint); } canvas.rotate(30.widhth / 2.widhth / 2); }}Copy the code
After the change:
/ * ** Draw scale * @param canvas * /private void drawScale(Canvas canvas) { mPaint.setStyle(Paint.Style.FILL); for(int i=0;i<12;i++) { if (i % 3 = =0) {// The corresponding line length points are 12, 3, 6, 9 canvas.drawLine(widhth / 2-padding .padding.widhth / 2 -padding.padding + 4 + 15.mPaint); }else{ canvas.drawLine(widhth / 2-padding .padding.widhth / 2-padding.padding + 4 + 8.mPaint); } canvas.rotate(30.widhth / 2.widhth / 2); }}Copy the code
I’m going to kill the hour hand at 3 and set the seconds at 15:
Thank you for asking questions in the group