首页 > 系统 > Android > 正文

Android实现简单时钟View的方法

2019-12-12 02:13:24
字体:
来源:转载
供稿:网友

通过Canvas的平移与旋转简化绘图逻辑是一个非常有用的技巧,下面的时钟view就是利用这个方法完成的,省去了使用三角函数计算坐标的麻烦。

clock

package com.example.swt369.simpleclock;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.View;import android.view.ViewTreeObserver;import android.widget.Toast;import java.util.Calendar;/** * Created by swt369 on 2017/8/20. */public class ClockView extends View {  //时钟各个部件的长度  private float mScaleLengthLong;  private float mScaleLengthShort;  private float mTickLengthHour;  private float mTickLengthMinute;  private float mTickLengthSecond;  //view的宽和高  private float mWidth;  private float mHeight;  //时钟半径  private float mRadius;  //绘制各部件时用的Paint  private Paint mPaintScaleLong;  private Paint mPaintScaleShort;  private Paint mPaintOutline;  private Paint mPaintNum;  private Paint mPaintTickHour;  private Paint mPaintTickMinute;  private Paint mPaintTickSecond;  private ViewTreeObserver.OnPreDrawListener onPreDrawListener;  public ClockView(final Context context, @Nullable AttributeSet attrs) {    super(context, attrs);    onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {      //获取view宽高并计算各个部件的长度      @Override      public boolean onPreDraw() {        mWidth = getMeasuredWidth();        mHeight = getMeasuredHeight();        mRadius = Math.min(mWidth,mHeight) / 2 * 0.95f;        mScaleLengthLong = mRadius * 0.1f;        mScaleLengthShort = mRadius * 0.05f;        mTickLengthHour = mRadius * 0.3f;        mTickLengthMinute = mRadius * 0.45f;        mTickLengthSecond = mRadius * 0.6f;        return true;      }    };    getViewTreeObserver().addOnPreDrawListener(onPreDrawListener);    //点击显示具体时间    setOnClickListener(new OnClickListener() {      @Override      public void onClick(View v) {        Calendar calendar = Calendar.getInstance();        String time = String.format("当前时间:%02d:%02d:%02d",            calendar.get(Calendar.HOUR_OF_DAY),calendar.get(Calendar.MINUTE),calendar.get(Calendar.SECOND));        Toast.makeText(context,time, Toast.LENGTH_SHORT).show();      }    });    //初始化所有Paint对象    initializePaints();  }  @Override  protected void onDraw(Canvas canvas) {    if(mWidth == 0 || mHeight == 0){      return;    }    if(onPreDrawListener != null){      getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener);      onPreDrawListener = null;    }    //绘制时钟    drawClock(canvas);    //一秒后重绘    postInvalidateDelayed(1000);  }  private void drawClock(Canvas canvas) {    //保存原始状态    canvas.save();    //将坐标系原点移到中心,并逆时针旋转90度。完成后x轴朝上。    canvas.translate(mWidth / 2,mHeight / 2);    canvas.rotate(-90);    //画外围轮廓    canvas.drawCircle(0,0,mRadius, mPaintOutline);    //画刻度    for(int i = 0 ; i < 12 ; i++){      String num = String.valueOf(i == 0 ? 12 : i);      if(i % 3 == 0){        canvas.drawLine(mRadius,0,mRadius - mScaleLengthLong,0, mPaintScaleLong);      }else {        canvas.drawLine(mRadius,0,mRadius - mScaleLengthShort,0, mPaintScaleShort);      }      canvas.drawText(num,mRadius - mScaleLengthLong - mPaintNum.measureText(num) * 2,0, mPaintNum);      //顺时针旋转30度      canvas.rotate(30);    }    Calendar calendar = Calendar.getInstance();    //画时针    int hour = calendar.get(Calendar.HOUR);    canvas.save();    canvas.rotate(hour * 30);    canvas.drawLine(0,0,mTickLengthHour,0,mPaintTickHour);    canvas.restore();    //画分针    int minute = calendar.get(Calendar.MINUTE);    canvas.save();    canvas.rotate(minute * 6);    canvas.drawLine(0,0,mTickLengthMinute,0,mPaintTickMinute);    canvas.restore();    //画秒针    int second = calendar.get(Calendar.SECOND);    canvas.save();    canvas.rotate(second * 6);    canvas.drawLine(0,0,mTickLengthSecond,0,mPaintTickSecond);    canvas.restore();    //恢复原始状态    canvas.restore();  }  private void initializePaints(){    mPaintScaleLong = new Paint();    mPaintScaleLong.setAntiAlias(true);    mPaintScaleLong.setStrokeWidth(5);    mPaintScaleShort = new Paint();    mPaintScaleShort.setAntiAlias(true);    mPaintScaleShort.setStrokeWidth(3);    mPaintOutline = new Paint();    mPaintOutline.setStyle(Paint.Style.STROKE);    mPaintOutline.setAntiAlias(true);    mPaintOutline.setStrokeWidth(5);    mPaintNum = new Paint();    mPaintNum.setTextSize(30);    mPaintTickHour = new Paint();    mPaintTickHour.setAntiAlias(true);    mPaintTickHour.setStrokeWidth(6);    mPaintTickMinute = new Paint();    mPaintTickMinute.setAntiAlias(true);    mPaintTickMinute.setStrokeWidth(4);    mPaintTickSecond = new Paint();    mPaintTickSecond.setAntiAlias(true);    mPaintTickSecond.setStrokeWidth(2);  }}

代码注释已经比较详细了,下面把比较重要的部分再说明一下:

//保存原始状态canvas.save();//省略中间部分...//恢复原始状态canvas.restore();

在绘图开始时调用canvas.save(),可以保存下未经任何平移、旋转操作的原始画布状态。在所有绘图工作完成后调用canvas.restore(),可以恢复到上一次保存的状态(类似进出栈的感觉)。

//将坐标系原点移到中心,并逆时针旋转90度。完成后x轴朝上。canvas.translate(mWidth / 2,mHeight / 2);canvas.rotate(-90);

画布的平移与旋转可能比较抽象,可以想象成坐标系的平移与旋转。调用canvas.translate(dx,dy)相当于将坐标原点向x,y方向移动了dx,dy的距离,调用canvas.rotate(degree)相当于坐标系顺时针旋转了degree°。

//画刻度for(int i = 0 ; i < 12 ; i++){  String num = String.valueOf(i == 0 ? 12 : i);  if(i % 3 == 0){    canvas.drawLine(mRadius,0,mRadius - mScaleLengthLong,0, mPaintScaleLong);  }else {    canvas.drawLine(mRadius,0,mRadius - mScaleLengthShort,0, mPaintScaleShort);  }  canvas.drawText(num,mRadius - mScaleLengthLong - mPaintNum.measureText(num) * 2,0, mPaintNum);  //顺时针旋转30度  canvas.rotate(30);}

根据i值决定是画大刻度(90°的倍数)还是小刻度。每次画完一条之后将坐标系顺时针旋转30°,这样保证每次画的刻度线的坐标不变。

//一秒后重绘postInvalidateDelayed(1000);

作用是在1000ms(1s)后再次执行View的draw流程,产生指针运动的效果。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表