看到一加OS 上的表盘不错,所以仿写一个同时复习一下自定义View的内容。
一个圆形白色表盘,12个刻度点,3个指针,一个中心点,一个当前时间指示点。
很简单,直接覆写onDraw 方法 ,在canvas 上绘制内容即可,都是最基本的canvas API 绘制操作,绘制过程都是基本的API调用。
public void drawLine(float startX, float startY, float stopX, float stopY,
@NonNull Paint paint)
public void drawPoint(float x, float y, @NonNull Paint paint)
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint)
public void translate(float dx, float dy)
旋转画布
public void rotate(float degrees)
结合平移和旋转操作让简化坐标计算。
基本Handler操作,每隔一秒执行一次重绘操作。
注意点:自动开启/停止绘制操作的时机选在View的 onAttachedToWindow/onDetachedFromWindow
回调中,简化使用。
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.util.Log;
import android.view.View;
import java.util.Calendar;
/**
* @author Bridgeliang
* @date 2019/5/19
*/
public class ClockView extends View {
private static final String TAG = "ClockView";
private Paint mPaint;
private int width, height, radius;
private int lenDot, lenHour, lenMin, lenSec;
private int minSqureSize;
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);
init();
}
private void init() {
mPaint = new Paint();
minSqureSize = dip2px(getContext(), 200);
}
private void drawBasicView(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAntiAlias(true);
//绘制表盘
mPaint.setColor(Color.WHITE);
canvas.translate(width / 2, height / 2);
canvas.rotate(-90);
canvas.drawCircle(0, 0, radius, mPaint);
//绘制12个刻度点
mPaint.setColor(Color.GRAY);
mPaint.setStrokeWidth(dip2px(getContext(), 5));
canvas.save();
for (int i = 0; i < 12; i++) {
canvas.drawPoint(0, -lenDot, mPaint);
canvas.rotate(30);
}
canvas.restore();
//绘制逻辑
int[] time = getCurrentTime();
int hour = time[0];
int min = time[1];
int sec = time[2];
float hourRotateDegree = (hour * 3600f + min * 60f + sec) / (12f * 60f * 60f) * 360f;
float minRotateDegree = min * 1f / 60f * 360f;
float secRotateDegree = sec * 1f / 60f * 360f;
//时针
mPaint.setColor(Color.BLACK);
canvas.save();
canvas.rotate(hourRotateDegree);
canvas.drawLine(0, 0, lenHour, 0, mPaint);
canvas.restore();
//分针
canvas.save();
canvas.rotate(minRotateDegree);
canvas.drawLine(0, 0, lenMin, 0, mPaint);
canvas.restore();
//秒针
canvas.save();
mPaint.setStrokeWidth(dip2px(getContext(), 1));
mPaint.setColor(Color.RED);
canvas.rotate(secRotateDegree);
canvas.drawLine(0, 0, lenSec, 0, mPaint);
canvas.restore();
//绘制中心点
mPaint.setColor(Color.GRAY);
canvas.drawCircle(0, 0, dip2px(getContext(), 4), mPaint);
//绘制边缘点
mPaint.setColor(Color.GREEN);
canvas.save();
canvas.rotate(hourRotateDegree);
canvas.drawCircle(radius, 0, dip2px(getContext(), 8), mPaint);
canvas.restore();
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
invalidate();
sendMessageDelayed(obtainMessage(), 1000);
}
};
private int[] getCurrentTime() {
int[] time = new int[3];
time[0] = Calendar.getInstance().get(Calendar.HOUR);
time[1] = Calendar.getInstance().get(Calendar.MINUTE);
time[2] = Calendar.getInstance().get(Calendar.SECOND);
Log.e(TAG, String.format("%d:%d:%d", time[0], time[1], time[2]));
return time;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Log.e(TAG, "onAttachedToWindow: ");
startTimer();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.e(TAG, "onDetachedFromWindow: ");
stopTimer();
}
private void startTimer() {
mHandler.removeCallbacksAndMessages(null);
mHandler.obtainMessage().sendToTarget();
}
private void stopTimer() {
mHandler.removeCallbacksAndMessages(null);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
radius = Math.min(width, height) / 2 - dip2px(getContext(), 8);
lenDot = radius * 7 / 8;
lenMin = radius * 5 / 8;
lenHour = radius * 3 / 8;
lenSec = radius * 6 / 8;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.e(TAG, "onMeasure: ");
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//支持wrap_content
if (widthMode == MeasureSpec.AT_MOST)
widthSize = minSqureSize;
if (heightMode == MeasureSpec.AT_MOST)
heightSize = minSqureSize;
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.e(TAG, "onLayout: ");
}
@Override
protected void onDraw(Canvas canvas) {
Log.e(TAG, "onDraw: ");
drawBasicView(canvas);
}
private int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}