Android打造属于自己的时间钟表

1、概述

本文主要讲解的是如何自定义一个时间钟表,通过简单的练习可以简单学习Android当中自定义view的一些常用绘图技巧,优化android绘图操作。言归正传,首先看下我们需要实现的效果:

当我们看到这个效果的时候脑子里应该有一定的思路了,我们应该把它分解成以下几个步骤:
1、仪表盘(圆)
2、刻度线(长 中 短)
3、刻度值(1-12)
4、指针(时  分  秒)
5、移动指针,计算指针位置
现在我们已经很清楚自己的思路了,那么我们一个一个来。

第一步:1、自定义View的属性,首先在res/values/  下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。

<span style="font-size:14px;"> <declare-styleable name="ClockView"> 

  <attr name="mRadius" format="dimension"/>
  <attr name="mCircleColor" format="color"/>
  <attr name="mCircleWidth" format="dimension"/>
  <attr name="mTextSize" format="dimension"/>
  <attr name="mTextColor" format="color"/>
  <attr name="mBigScaleColor" format="color"/>
  <attr name="mMiddlecaleColor" format="color"/>
  <attr name="mSmallScaleColor" format="color"/>
  <attr name="mHourHandColor" format="color"/>
  <attr name="mMinuteHandColor" format="color"/>
  <attr name="mSecondHandColor" format="color"/>
  <attr name="mHourHandWidth" format="dimension"/>
  <attr name="mMinuteHandWidth" format="dimension"/>
  <attr name="mSecondHandWidth" format="dimension"/> 

 </declare-styleable></span>

我们定义了钟表的半径,背景颜色 ,刻度值(大,中,小)的颜色及指针(时分秒)的颜色和宽度。

然后自定义一个class类 为ClockView,在MainActivity的布局中引用:

<span style="font-size:14px;"> <com.dalong.customviewstudy.view.ClockView
  app:mSecondHandColor="@color/colorAccent"
  app:mCircleColor="@android:color/white"
  app:mBigScaleColor="@android:color/black"
  app:mMiddlecaleColor="@android:color/black"
  app:mSmallScaleColor="@color/colorAccent"
  app:mHourHandColor="@android:color/black"
  app:mMinuteHandColor="@android:color/black"
  app:mTextColor="@android:color/black"
  app:mHourHandWidth="13dp"
  app:mSecondHandWidth="5dp"
  app:mMinuteHandWidth="8dp"
  app:mTextSize="16sp"
  android:layout_centerInParent="true"
  android:layout_width="match_parent"
  android:layout_height="match_parent" /></span>

2、在自定义View的构造方法中,获得我们的自定义的样式

<span style="font-size:14px;"> //文字画笔对象
 private Paint mTextPaint; 

 //圆,指针,刻度画笔
 private Paint mPaint; 

 //半径
 public float mRadius; 

 //外圆的颜色
 public int mCircleColor; 

 // 外圆的宽度
 public float mCircleWidth; 

 //文字的大小
 public float mTextSize; 

 //文字的颜色
 public int mTextColor; 

 //大刻度颜色
 public int mBigScaleColor; 

 //中刻度
 public int mMiddlecaleColor; 

 //小刻度颜色
 public int mSmallScaleColor; 

 //时针颜色
 public int mHourHandColor; 

 //分针颜色
 public int mMinuteHandColor; 

 //秒针颜色
 public int mSecondHandColor; 

 //时针宽度
 public float mHourHandWidth; 

 //分针宽度
 public float mMinuteHandWidth; 

 //秒针宽度
 public float mSecondHandWidth; 

 //控件宽度
 public int mWidth; 

 //控件高度
 public int mHeght; 

 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); 

  TypedArray typedArray=context.obtainStyledAttributes(attrs, R.styleable.ClockView);
  mRadius=typedArray.getDimension(R.styleable.ClockView_mRadius,400);
  mCircleColor=typedArray.getColor(R.styleable.ClockView_mCircleColor, Color.WHITE);
  mCircleWidth=typedArray.getDimension(R.styleable.ClockView_mCircleWidth,20);
  mTextSize=typedArray.getDimension(R.styleable.ClockView_mCircleWidth,40);
  mTextColor=typedArray.getColor(R.styleable.ClockView_mTextColor,Color.DKGRAY);
  mBigScaleColor=typedArray.getColor(R.styleable.ClockView_mBigScaleColor,Color.BLACK);
  mSmallScaleColor=typedArray.getColor(R.styleable.ClockView_mSmallScaleColor,Color.RED);
  mMiddlecaleColor=typedArray.getColor(R.styleable.ClockView_mMiddlecaleColor,Color.BLACK);
  mHourHandColor=typedArray.getColor(R.styleable.ClockView_mHourHandColor,Color.BLACK);
  mMinuteHandColor=typedArray.getColor(R.styleable.ClockView_mMinuteHandColor,Color.BLACK);
  mSecondHandColor=typedArray.getColor(R.styleable.ClockView_mSecondHandColor,Color.BLACK);
  mHourHandWidth=typedArray.getDimension(R.styleable.ClockView_mHourHandWidth,20);
  mMinuteHandWidth=typedArray.getDimension(R.styleable.ClockView_mMinuteHandWidth,10);
  mSecondHandWidth=typedArray.getDimension(R.styleable.ClockView_mSecondHandWidth,5); 

  mPaint=new Paint();
  mPaint.setAntiAlias(true);
  mPaint.setStyle(Paint.Style.STROKE); 

  mTextPaint=new Paint();
  mTextPaint.setAntiAlias(true);
  mTextPaint.setStyle(Paint.Style.STROKE);
  mTextPaint.setTextSize(mTextSize);
  mTextPaint.setColor(mTextColor); 

 }
</span>

3、我们重写onDraw,onMesure调用系统提供的:

onMeure方法

<span style="font-size:14px;"> @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(measureSize(widthMeasureSpec),measureSize(heightMeasureSpec));
 } 

 private int measureSize(int mMeasureSpec) {
  int result;
  int mode=MeasureSpec.getMode(mMeasureSpec);
  int size=MeasureSpec.getSize(mMeasureSpec);
  if(mode==MeasureSpec.EXACTLY){
   result=size;
  }else{
   result=400;
   if(mode==MeasureSpec.AT_MOST){
    result=Math.min(result,size);
   }
  }
  return result;
 }</span>

onDraw方法

<span style="font-size:14px;"> @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  //设置宽高、半径
  mWidth=getMeasuredWidth()-getPaddingLeft()-getPaddingRight();
  mHeght=getMeasuredHeight()-getPaddingBottom()-getPaddingTop();
  mRadius=Math.min(mWidth/2,mHeght/2);
  //首先绘制圆
  drawCircle(canvas);
  //绘制刻度
  drawScale(canvas);
  //绘制指针
  drawPointer(canvas);
  //发送消息刷新ui
  handler.sendEmptyMessageDelayed(START_CLOCK,1000);
 }
</span>

其中最核心的代码就是这三个方法:

<span style="font-size:14px;">  //首先绘制圆
  drawCircle(canvas);
  //绘制刻度
  drawScale(canvas);
  //绘制指针
  drawPointer(canvas);</span> 

首先讲第一个方法:

<span style="font-size:14px;"> /**
  * 画圆
  * @param canvas
  */
 private void drawCircle(Canvas canvas) {
  mPaint.setStrokeWidth(mCircleWidth);
  mPaint.setStyle(Paint.Style.FILL);
  mPaint.setColor(mCircleColor);
  canvas.drawCircle(mWidth/2,mHeght/2,mRadius,mPaint);
 }</span>

这个方法其实很简单给我们的画笔设置我们自定义的样式之后取中心为圆心,以我们设定的半径画圆,这里设置的是Paint.Style.FILL一个实心的。也可以设置一个空心的。看下我们执行这个方法后的效果:

第二方法:

<span style="font-size:14px;"> /**
  * 刻度和文字
  * @param canvas
  */
 private void drawScale(Canvas canvas) { 

  for (int i=0;i<60;i++){
   //设置大刻度
   if(i==0||i==15||i==30||i==45){
    mPaint.setStrokeWidth(6);
    mPaint.setColor(mBigScaleColor);
    canvas.drawLine(mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2,
      mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2+60,mPaint);
    String scaleTv=String.valueOf(i==0?12:i/5);
    canvas.drawText(scaleTv,mWidth/2-mTextPaint.measureText(scaleTv)/2,
      mHeght/2-mWidth/2+mCircleWidth/2+95,mTextPaint);
   }else if (i==5||i==10||i==20||i==25||i==35||i==40||i==50||i==55)
   //设置中刻度
   {
    mPaint.setStrokeWidth(4);
    mPaint.setColor(mMiddlecaleColor);
    canvas.drawLine(mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2,
      mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2+40,mPaint);
    String scaleTv=String.valueOf(i/5);
    canvas.drawText(scaleTv,mWidth/2-mTextPaint.measureText(scaleTv)/2,
      mHeght/2-mWidth/2+mCircleWidth/2+75,mTextPaint); 

   }else
   //设置小刻度
   {
    mPaint.setColor(mSmallScaleColor);
    mPaint.setStrokeWidth(2);
    canvas.drawLine(mWidth/2,mHeght/2-mWidth/2+mCircleWidth/2,
      mWidth/2,mHeght/2-mWidth/2+mCircleWidth+30,mPaint);
   }
   canvas.rotate(6,mWidth/2,mHeght/2);
  }
 }</span>

这个方法代码看起来也没有什么主要是把一个圆分成60份,因为我们钟表上是有60个刻度,其中设置了4个大刻度分别为0,15,30,45.分别对应的钟表中12点  3点  6点和9点,如果这个地方你有什么疑惑的吧你可以看看你的手表或者钟表就明白了,同时里面也设置了8个中等刻度分别为5,10,20,25,35,40,50,55为中刻度,其实对应的就是1,2,4,5,7,8,10,11点。这里主要是自己觉得这样分明好看而已,如果没有强迫症的你可以直接设置都是大刻度就可以了。其他的都为小刻度,根据自己在attr设置的颜色和尺寸分别设置画笔paint来绘制就可以了。看下我们的效果变成了这样子:

第三个方法就是绘制指针:

<span style="font-size:14px;"> /**
  * 绘制指针
  * @param canvas
  */
 private void drawPointer(Canvas canvas) {
  Calendar mCalendar=Calendar.getInstance();
  //获取当前小时数
  int hours = mCalendar.get(Calendar.HOUR);
  //获取当前分钟数
  int minutes = mCalendar.get(Calendar.MINUTE);
  //获取当前秒数
  int seconds=mCalendar.get(Calendar.SECOND); 

  mPaint.setStrokeCap(Paint.Cap.ROUND);
  //绘制时针
  canvas.save();
  mPaint.setColor(mHourHandColor);
  mPaint.setStrokeWidth(mHourHandWidth);
  //这里计算时针需要旋转的角度 实现原理是计算出一共多少分钟除以60计算出真实的小时数(带有小数,为了更加准确计算度数),已知12小时是360度,现在求出了实际小时数比例求出角度
  Float hoursAngle = (hours * 60 + minutes) / 60f / 12f * 360;
  canvas.rotate(hoursAngle, mWidth / 2, mHeght / 2);
  canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.5f, mWidth / 2, mHeght / 2 + mWidth/2f*0.15f, mPaint);
  canvas.restore(); 

  //绘制分针
  canvas.save();
  mPaint.setColor(mMinuteHandColor);
  mPaint.setStrokeWidth(mMinuteHandWidth);
  //这里计算分针需要旋转的角度 60分钟360度,求出实际分钟数所占的度数
  Float minutesAngle = (minutes*60+seconds) / 60f/ 60f * 360;
  canvas.rotate(minutesAngle, mWidth / 2, mHeght / 2);
  canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.7f, mWidth / 2, mHeght / 2 + mWidth/2f*0.15f, mPaint);
  canvas.restore(); 

  //绘制中间的圆圈
  canvas.save();
  mPaint.setColor(mSecondHandColor);
  mPaint.setStrokeWidth(mSecondHandWidth);
  mPaint.setStyle(Paint.Style.FILL);
  canvas.drawCircle(mWidth/2,mHeght/2,20,mPaint);
  canvas.restore(); 

  //绘制秒针
  canvas.save();
  mPaint.setColor(mSecondHandColor);
  mPaint.setStrokeWidth(mSecondHandWidth);
  mPaint.setStyle(Paint.Style.STROKE);
  //这里计算秒针需要旋转的角度 60秒360度,求出实际秒数所占的度数
  Float secondAngle = seconds/60f*360;
  canvas.rotate(secondAngle, mWidth / 2, mHeght / 2);
  canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.8f, mWidth / 2, mHeght / 2 + mWidth/2f*0.2f, mPaint);
  canvas.restore(); 

 }</span>

其实这个方法我注释已经写的很详细了,首先我们需要获取到当前的时间,这个大家都是经常写的没啥问题。主要就是如何设定时分秒指针的位置才是关键。这里我使用一个很巧妙的方法,让绘制变得简单了些。
先看下绘制时针:

<span style="font-size:14px;"> //绘制时针
    canvas.save();
    mPaint.setColor(mHourHandColor);
    mPaint.setStrokeWidth(mHourHandWidth);
    //这里计算时针需要旋转的角度 实现原理是计算出一共多少分钟除以60计算出真实的小时数(带有小数,为了更加准确计算度数),已知12小时是360度,现在求出了实际小时数比例求出角度
    Float hoursAngle = (hours * 60 + minutes) / 60f / 12f * 360;
    canvas.rotate(hoursAngle, mWidth / 2, mHeght / 2);
    canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.5f, mWidth / 2, mHeght / 2 + mWidth/2f*0.15f, mPaint);
    canvas.restore();</span>

前面三行代码就不详细说了,Canvas.save方法作用就是将之前所有已经绘制的图片保存起来。为后续操作在新的图层上操作。和photoshop有点一个意思。大家都知道当我们获取到当前小时数了以后我们就应该直接把时针指到对应的时数上吗?肯定不是吧,比如是3点半时针是指到3与4之间的位置对吧。所以我们这里需要获取到当前的分钟数再加上小时数才是真实的当前小时数(这里其实秒针也需要计算的,但是这里忽略不计了,如果你比我还强迫症的话可以加上)。当我们知道当前实际的小时数的时候,就很简单了,因为我们知道一圈360度平均分了12小时,这个别告诉我不知道啊,要是这个常识都不知道,你去面壁吧,所以只要360/12*真实的小时数就是需要旋转的角度。这么想是不是很简单了。计算出角度以后先吧canvas.rotate旋转这个角度在绘制一个直线就ok了,哈哈哈,是不是so esey,其他分针和秒针一样的道理,这里就不多说了,看代码直接能看懂的。

<span style="font-size:14px;">
    //绘制分针
    canvas.save();
    mPaint.setColor(mMinuteHandColor);
    mPaint.setStrokeWidth(mMinuteHandWidth);
    //这里计算分针需要旋转的角度 60分钟360度,求出实际分钟数所占的度数
    Float minutesAngle = (minutes*60+seconds) / 60f/ 60f * 360;
    canvas.rotate(minutesAngle, mWidth / 2, mHeght / 2);
    canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.7f, mWidth / 2, mHeght / 2 + mWidth/2f*0.15f, mPaint);
    canvas.restore(); 

    //绘制中间的圆圈
    canvas.save();
    mPaint.setColor(mSecondHandColor);
    mPaint.setStrokeWidth(mSecondHandWidth);
    mPaint.setStyle(Paint.Style.FILL);
    canvas.drawCircle(mWidth/2,mHeght/2,20,mPaint);
    canvas.restore(); 

    //绘制秒针
    canvas.save();
    mPaint.setColor(mSecondHandColor);
    mPaint.setStrokeWidth(mSecondHandWidth);
    mPaint.setStyle(Paint.Style.STROKE);
    //这里计算秒针需要旋转的角度 60秒360度,求出实际秒数所占的度数
    Float secondAngle = seconds/60f*360;
    canvas.rotate(secondAngle, mWidth / 2, mHeght / 2);
    canvas.drawLine(mWidth / 2, mHeght / 2 - mWidth/2f*0.8f, mWidth / 2, mHeght / 2 + mWidth/2f*0.2f, mPaint);
    canvas.restore();</span>

其中有个绘制中间的圆圈是我的强迫症所致,觉得中间加个圆圈好看点。执行这个方法后我们的效果就成这样了:

哈哈下面就剩下最后一步了,就是让指针动起来,没错就是动起来,其实大家也在意了我们绘制指针的时候是在方法里直接获取了当前时间设置指针的位置的,所以说只要我们搞个定时器一秒刷新下ui就大功告成了,这里就搞个hander发个空消息。

private Handler handler=new Handler(){
  @Override
  public void handleMessage(Message msg) {
    super.handleMessage(msg);
    switch (msg.what){
      case START_CLOCK:
        //更新时分秒
        invalidate();
        //每隔1秒更新一次
        handler.sendEmptyMessageDelayed(START_CLOCK,1000);
        break;
    } 

  }
};

就成了下面的效果了:

是不是很简单呢?附上github:https://github.com/dalong982242260/CustomViewStudy

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

(0)

相关推荐

  • Android 桌面Widget开发要点解析(时间日期Widget)

    最近需要编写一个日期时间的桌面Widget用来关联日历程序,以前很少写桌面Widget.对这方面技术不是很熟悉,今天花时间重新整理了一下,顺便把编写一个简单时间日期程序过程记录下来. 桌面Widget其实就是一个显示一些信息的工具(现在也有人开发了一些有实际操作功能的widget.例如相机widget,可以直接桌面拍照).不过总的来说,widget主要功能就是显示一些信息.我们今天编写一个很简单的作为widget,显示时间.日期.星期几等信息.需要显示时间信息,那就需要实时更新,一秒或者一分钟更

  • android获取时间差的方法

    本文实例讲述了android获取时间差的方法.分享给大家供大家参考.具体分析如下: 有些时候我们需要获取当前时间和某个时间之间的时间差,这时如何获取呢? 1. 引用如下命名空间: import java.util.Date; import android.text.format.DateFormat; 2. 设置时间格式: SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 3. 获取时间: Date c

  • android计时器,时间计算器的实现方法

    需求:默认为"00:00:00",点击开始按钮时清零后开始计时,出现如10:28:34.点击停止的时候停止计时.问题:使用Calendar DateFormat的方法,不设置时区获取到的小时是本地时区的(东八区的就是8),设置成GMT标准时区获取到的时间是12小时(12:00:00),设置24小时制无效.在开始时间加减各种小时都无效,而且计时只能到12小时就自动跳上去了,始终无法出现默认状态00:00:00开始计时的效果.尝试各种时间设置方法无效后只能自己写一个根据秒数转换时间格式字符

  • jquery+html5制作超酷的圆盘时钟表

    自己封装的一个用HTML5+jQuery写的时钟表 代码: <!DOCTYPE html PUBLIC "-//W3C//h2D XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/h2D/xhtml1-transitional.h2d"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <

  • 详解JavaScript的Date对象(制作简易钟表)

    JS提供了Date类型来处理时间和日期.Date类型内置一系列获取和设置日期时间信息的方法.下面我们简单的 概述一下这个Date类型.        大概看了一下Date类型的方法,下面给出: 上面的方法自己尝试即可,我只简单的演示一下JS正确输出的格式: var today=new Date();//创建一个时间日期对象 document.write("<h4>下面的是世界标准的时间输出:</h4>"); document.write(today+"

  • 利用css+原生js制作简单的钟表

    利用css+原生js制作简单的钟表.效果如下所示 实现该效果,分三大块:html.javascript.css html部分 html部分比较简单,定义一个clock的div,内部有原点.时分秒针.日期以及时间,至于钟表上的刻度.数字等元素,因为量比较多,采用jvascript生成 <!doctype html> <html> <head> <meta charset="UTF-8"> <link rel='stylesheet'

  • C++实现图形界面时钟表盘代码

    本文实例讲述了C++实现图形界面时钟表盘代码,分享给大家供大家参考. 具体实现代码如下: 复制代码 代码如下: //POINT的数组可以这么用      POINT pt[]={          0, 450,          225,390,          390,225,          450,0,          390,-225,          225,-390,          0,-450,          -225,-390,          -390,-2

  • android 默认时间格式修改方法

    在android使用中,遇到修改默认时间格式时,总是束手无策,本文将以此问题提供解决方案,需要了解的朋友可以参考下 语言为英语时,默认的时间格式为mm/DD/yyyy,请问怎么将默认时间格式修改为:DD/mm/yyyy,不知道是在framework层给初始化的还是编译的时候给的初始值,哪位大侠知道怎么该? 1.修改文件alps\frameworks\base\packages\SettingsProvider\res\values\defaults.xml 增加代码<string name=&quo

  • 解析android中系统日期时间的获取

    复制代码 代码如下: import    java.text.SimpleDateFormat; SimpleDateFormat    formatter    =   new    SimpleDateFormat    ("yyyy年MM月dd日    HH:mm:ss     ");     Date    curDate    =   new    Date(System.currentTimeMillis());//获取当前时间     String    str    =

  • Android获取通话时间实例分析

    本文章总结了一段Android获取通话时间程序代码,有需要的朋友可参考一下. 我们知道安卓系统中通话时长应该是归Callog管,所以建议去查查ContactProvider,或者是TelephonyProvider Service测试 可以的通话开始的时候启动Service 记录当前时间A, 然后stopSelf(); 另外在通话结束的时候再次启动一下Service,再次获得当前时间B, 然后把时间A和B进行比较处理 String time = Long.toString(比较后处理的时间) 然

随机推荐