本文实例为大家分享了android view实现钟表的具体代码,供大家参考,具体内容如下

先看效果图:

自定义view大家肯定已经不陌生了,所以直接今天直接步入正题:如何利用canvas去绘制出一个钟表

当然绘制之前我们必须进行测量(重写onMeasure),根据自己的规则去测量,这暂时是将控件限制为一个正方形。

首先我们先把钟表分解,看它由哪几部分组成。如上图:钟表包括表盘(刻度)和表针还有文字构成。

分清结构之后我们再明确canvas需要画什么,表盘的构成其实就是外层一个圆,然后上面是有规律的线段,表针就是三个长短不一的线段,再加上12个钟点文字。这样一分析是不是发现调用canvas的drawCircle、drawLine和drawText就可以完成钟表的绘制了。

既然明确了我们绘制所需要的方法,那么就开始重头戏了,告诉canvas在哪绘制这些零件。

最外层的圆是最简单的,我们只需要以控件的中心为圆心,控件的宽度一半为半径画一个圆就可以了。

接下来就是难点一了,这些刻度怎么办呢,其实我们不难发现其中的规律,每个刻度之间的弧度是一样的,那这样我们是不是可以通过旋转画布就可以实现这些刻度的绘制呢,答案是肯定的。

难点二,文字又该如何绘制,难道也通过旋转画布吗,但是你想一下,假如通过旋转画布去绘制文字,那有些文字可是会颠倒的,这并不是我们想要的结果,那该怎么办,这时候我们只能通过数学计算老老实实的计算每个文字的起始坐标,这些坐标并没有想象中的复杂,我们可以根据中心点的位置和偏移角度(当然还需要考虑文字的宽度)算出。

难点三,绘制表针,其实文字绘制出来,那么同样可以根据中心点和偏移角度算出表针的起始坐标和结束坐标

表心就是一个实体的圆,这个就简单了。

好像还没说时分秒是怎么确定的,这当然是通过系统时间获取的了。说到这里似乎一个静态钟表已经绘制出来了,接下来让它动起来就可以了。在这我们启动一个线程,让它隔一秒钟进行一次重绘即可。

下面我直接贴一下代码把,代码是用kotlin实现(这不是重点)的

package com.example.commonui.widget

import android.annotation.SuppressLint

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.view.View

import java.util.*

/**

* Created by zhang on 2017/12/20.

*/

class ClockView(context: Context?, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {

companion object {

private const val DEFAULT_WIDTH = 200 //默认宽度

}

private lateinit var mBlackPaint: Paint//黑色画笔

private lateinit var mRedPaint: Paint //红色画笔

private lateinit var mBlackPaint2: Paint//黑色画笔

private lateinit var mTextPaint: Paint

private var hour: Int? = null

private var minute: Int? = null

private var second: Int? = null

private val textArray = arrayOf("12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11")

private var refreshThread: Thread? = null

private var mHandler = @SuppressLint("HandlerLeak")

object : Handler() {

override fun handleMessage(msg: Message?) {

super.handleMessage(msg)

when (msg?.what) {

0 -> {

invalidate()

}

}

}

}

init {

initPaints()

}

/**

* 初始化画笔

*/

private fun initPaints() {

mBlackPaint = Paint()

with(mBlackPaint) {

color = Color.BLACK

strokeWidth = 5f

isAntiAlias = true

style = Paint.Style.STROKE

}

//用于画表心

mBlackPaint2 = Paint()

with(mBlackPaint2) {

color = Color.BLACK

isAntiAlias = true

style = Paint.Style.FILL

}

mRedPaint = Paint()

with(mRedPaint) {

color = Color.RED

strokeWidth = 5f

isAntiAlias = true

}

mTextPaint = Paint()

with(mTextPaint) {

color = Color.BLACK

textSize = 30f

isAntiAlias = true

}

}

override fun onDraw(canvas: Canvas?) {

super.onDraw(canvas)

//获取当前时间

getCurrentTime()

//先画最外层的圆圈

drawOuterCircle(canvas)

//画刻度

drawScale(canvas)

//绘制文字

drawTimeText(canvas)

//绘制表针

drawHand(canvas)

//绘制表心

drawCenter(canvas)

}

private fun getCurrentTime() {

val calendar = Calendar.getInstance()

hour = calendar.get(Calendar.HOUR)

minute = calendar.get(Calendar.MINUTE)

second = calendar.get(Calendar.SECOND)

}

private fun drawOuterCircle(canvas: Canvas?) {

mBlackPaint.strokeWidth = 5f

canvas?.drawCircle(measuredWidth / 2.toFloat(), measuredHeight / 2.toFloat(), (measuredWidth / 2 - 5).toFloat(), mBlackPaint)

}

private fun drawCenter(canvas: Canvas?) {

canvas?.drawCircle(measuredWidth / 2.toFloat(), measuredHeight / 2.toFloat(), 20f, mBlackPaint2)

}

private fun drawHand(canvas: Canvas?) {

drawSecond(canvas, mRedPaint)

mBlackPaint.strokeWidth = 10f

drawMinute(canvas, mBlackPaint)

mBlackPaint.strokeWidth = 15f

drawHour(canvas, mBlackPaint)

}

private fun drawTimeText(canvas: Canvas?) {

val textR = (measuredWidth / 2 - 50).toFloat()//文字构成的圆的半径

for (i in 0..11) {

//绘制文字的起始坐标

val startX = (measuredWidth / 2 + textR * Math.sin(Math.PI / 6 * i) - mTextPaint.measureText(textArray[i]) / 2).toFloat()

val startY = (measuredHeight / 2 - textR * Math.cos(Math.PI / 6 * i) + mTextPaint.measureText(textArray[i]) / 2).toFloat()

canvas?.drawText(textArray[i], startX, startY, mTextPaint)

}

}

private fun drawScale(canvas: Canvas?) {

var scaleLength: Float?

canvas?.save()

//0..59代表[0,59]

for (i in 0..59) {

if (i % 5 == 0) {

//大刻度

mBlackPaint.strokeWidth = 5f

scaleLength = 20f

} else {

//小刻度

mBlackPaint.strokeWidth = 3f

scaleLength = 10f

}

canvas?.drawLine(measuredWidth / 2.toFloat(), 5f, measuredWidth / 2.toFloat(), (5 + scaleLength), mBlackPaint)

canvas?.rotate(360 / 60.toFloat(), measuredWidth / 2.toFloat(), measuredHeight / 2.toFloat())

}

//恢复原来状态

canvas?.restore()

}

/**

* 绘制秒针

*/

private fun drawSecond(canvas: Canvas?, paint: Paint?) {

//秒针长半径 (表针会穿过表心 所以需要根据两个半径计算起始和结束半径)

val longR = measuredWidth / 2 - 60

val shortR = 60

val startX = (measuredWidth / 2 - shortR * Math.sin(second!!.times(Math.PI / 30))).toFloat()

val startY = (measuredWidth / 2 + shortR * Math.cos(second!!.times(Math.PI / 30))).toFloat()

val endX = (measuredWidth / 2 + longR * Math.sin(second!!.times(Math.PI / 30))).toFloat()

val endY = (measuredWidth / 2 - longR * Math.cos(second!!.times(Math.PI / 30))).toFloat()

canvas?.drawLine(startX, startY, endX, endY, paint)

}

/**

* 绘制分针

*/

private fun drawMinute(canvas: Canvas?, paint: Paint?) {

//半径比秒针小一点

val longR = measuredWidth / 2 - 90

val shortR = 50

val startX = (measuredWidth / 2 - shortR * Math.sin(minute!!.times(Math.PI / 30))).toFloat()

val startY = (measuredWidth / 2 + shortR * Math.cos(minute!!.times(Math.PI / 30))).toFloat()

val endX = (measuredWidth / 2 + longR * Math.sin(minute!!.times(Math.PI / 30))).toFloat()

val endY = (measuredWidth / 2 - longR * Math.cos(minute!!.times(Math.PI / 30))).toFloat()

canvas?.drawLine(startX, startY, endX, endY, paint)

}

/**

* 绘制时针

*/

private fun drawHour(canvas: Canvas?, paint: Paint?) {

//半径比秒针小一点

val longR = measuredWidth / 2 - 120

val shortR = 40

val startX = (measuredWidth / 2 - shortR * Math.sin(hour!!.times(Math.PI / 6))).toFloat()

val startY = (measuredWidth / 2 + shortR * Math.cos(hour!!.times(Math.PI / 6))).toFloat()

val endX = (measuredWidth / 2 + longR * Math.sin(hour!!.times(Math.PI / 6))).toFloat()

val endY = (measuredWidth / 2 - longR * Math.cos(hour!!.times(Math.PI / 6))).toFloat()

canvas?.drawLine(startX, startY, endX, endY, paint)

}

/**

* 进行测量

*/

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec)

val widthSpecMode = MeasureSpec.getMode(widthMeasureSpec)

val widthSpecSize = MeasureSpec.getSize(widthMeasureSpec)

val heightSpecMode = MeasureSpec.getMode(heightMeasureSpec)

val heightSpecSize = MeasureSpec.getSize(heightMeasureSpec)

val result = if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {

DEFAULT_WIDTH

} else {

Math.min(widthSpecSize, heightSpecSize)

}

setMeasuredDimension(result, result)

}

override fun onAttachedToWindow() {

super.onAttachedToWindow()

//启动线程 刷新界面

refreshThread = Thread(Runnable {

while (true) {

try {

Thread.sleep(1000)

mHandler.sendEmptyMessage(0)

} catch (e: InterruptedException) {

break

}

}

})

refreshThread?.start()

}

override fun onDetachedFromWindow() {

super.onDetachedFromWindow()

mHandler.removeCallbacksAndMessages(null)

//中断线程

refreshThread?.interrupt()

}

}

在这送上几点建议,1.尽量不要再ondraw里面创建对象,因为view可能会多次重绘,每次都创建新的对象会造成不必要的内存浪费

2.onmeasure方法会调用多次,请保证你的逻辑覆盖性,否则可能会出现没有按照你的预期得到宽高

3.线程的谨慎使用

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

Android自定义sleep图,android自定义view实现钟表效果相关推荐

  1. android自定义波浪图,Android自定义控件--波浪图控件

    今天给大家分享一个android的波浪图控件制作.具体效果如下图所示: 上次有个app使用了这个控件,感觉特别酷炫.今天讲解一下这个控件的思路分析与代码编写. 思路分析: 1.绘制波浪图 2.移动波浪 ...

  2. Android显示九宫图(自定义圆角,仿微信九宫格图)

    详细解析Android显示九宫图(自定义圆角,仿微信九宫格图) 这是一个自定义九宫格图片框架,里面有设置圆角大小,还有当图片一张的时候控件自定义的大小,图片的间隔,四张图片的时候图片自定义为两行两列等 ...

  3. android 自定义雷达图,Android自定义蛛网图(雷达图)

    前言 纸上得来终觉浅,绝知此事要躬行. 直接上代码 import android.content.Context; import android.graphics.Canvas; import and ...

  4. android层级关系图,画出 View 的层级 3D 图和树形图来分析层级关系

    HierarchyLayout 介绍 HierarchyLayout是一个自定义的ViewGroup工具,用于分析视图的层级关系,并画出层级树的结构图和可交互的3D 层级View: HiearchyL ...

  5. android 切凹凸图,Android实现边缘凹凸的View

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 转载 最近做项目的时候遇到一个卡劵的效果,由于自己觉得用图片来做的话可以会出现适配效果不好,再加上自己自定义view方面 ...

  6. android 字符串折线图,Android + MPAndroidChart 实现折线、柱形和饼状图

    本文讲讲怎么用 MPAndroidChart 框架实现折线图.柱形图.饼图,并且进行样式设置,达到和宣传图差不多的效果. 一开始看到 MPAndroidChart 的各种图表介绍图感觉很好看,但是直接 ...

  7. android 分辨率 切图,Android设计图(标注、切图)

    1.Android: 1)Android的单位是dp 2)分为ldpi/mdpi/hdpi/xhdpi/xxhdpi. 3)分辨率对应DPI ldpi  QVGA (240×320) mdpi  HV ...

  8. android 第三方扇形图,Android扇形图(饼状图)

    关于Android的图形控件,市场上是有很多的开源库的 我个人用的比较多的就是:MPAndroidChart,个人感觉挺好用的,但是有写时候,满足不了需求,就需要自己去写了. 先看看我们的效果图: T ...

  9. android 程序类图,Android模板设计模式之 - 构建整个应用的BaseActivity

    1. 模式介绍 模式的定义 定义一个操作中的算法的框架,而将一些步骤延迟到子类中.使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 模式的使用场景 多个子类有公有的方法,并且逻辑基本相 ...

最新文章

  1. Numpy掩码数组masked arrays,这一篇就够了
  2. java比go难学_为什么Go比Java快这么多?看完这个例子就懂
  3. 让容器跑得更快:CPU Burst 技术实践
  4. 求组合数python_给定一个序列求指定位数的排列组合数
  5. 如何修改ECShop发货单查询显示个数
  6. eureka server配置_springcloud项目搭建第三节:eureka集群
  7. 一不小心节约了 591 台机器!
  8. C++ 模板何时被实例化
  9. linux的创建线程池,Linux下通用线程池的创建与使用(上) (3)
  10. 【BZOJ5457】城市(线段树合并)
  11. Java面试官最爱问的垃圾回收机制,Java编程配置思路详解
  12. 苹果CMSv10_全站伪静态规则教程_宝塔Linux系统
  13. window上mySQL以及其可视化工具的安装
  14. 手机作为显示器及键鼠控制电脑棒(by quqi99)
  15. 软件测试-测试类型分类
  16. 安装ps教程,ps软件安装
  17. java指令工具_jvm 指令工具 jcmd 命令(Java多功能命令行)
  18. 数学建模 非线性规划
  19. 网站禁用鼠标右键php代码,JavaScript_网页禁用右键菜单和鼠标拖动选择方法小结,一、禁止鼠标右键菜单:- phpStudy...
  20. 产品 - 收藏集 - 掘金

热门文章

  1. 设置服务器系统的远程登录数量,设置服务器远程登录用户数量
  2. cad怎么调出科创易达绿化_【每日问答26】如何识别CAD图纸dwg格式版本?(内含往期)...
  3. ARM平台下独占访问指令LDREX和STREX的原理与使用详解
  4. 【安全实战】红队攻防技术
  5. 【建议收藏】这个工具专门用于寻找路由器中的安全漏洞
  6. 【PHP】循环 调用第三方API (curl ),性能优化
  7. 验证网站地址是否有效
  8. 递归和非递归实现二叉排序树(BST)的查找操作
  9. 108. 奇数码问题【思维 / 逆序对】
  10. 栈之顺序结构和链式结构