最近由于项目需要,需要自制一个钟表视图,并加一些业务逻辑,所以根据自定义一个View的步骤,自制了一个钟表,见下图:

下面是我自定义View的代码,参考了网上大神的代码,自己做了一些项目业务的逻辑,优化了一下整个View.

package com.hp.clock;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.View;/*** 时钟控件**/
public class ClockView extends View {private Time mCalendar;private final Drawable mHourHand; // 时钟时针图片private final Drawable mMinuteHand; // 时钟分针图片private final Drawable mSecondHand; // 时钟秒针图片private final Drawable mDial; // 时钟表盘图片private final int mDialWidth; // 组件宽度private final int mDialHeight; // 组件高度private boolean mAttached;private final Handler mHandler = new Handler();private float mSeconds; // 秒数,例如23sprivate float mMinutes; // 分钟数,例如12mprivate float mHour; // 小时数,例如5hprivate boolean mChanged;private final Context mContext;
//  private String mTimeZoneId;private int mTimeZoneOffset = 0; // 时差,单位是小时private boolean mNoSeconds = false; // 用来判断是否需要显示秒针public ClockView(Context context) {this(context, null);}public ClockView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ClockView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mContext = context;mCalendar = new Time();// 此处获取自定义的组件属性,定义在attr.xml中,用法详见activity_main.xml布局文件TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ClockView);mDial = a.getDrawable(R.styleable.ClockView_dialogDrawable);mHourHand = a.getDrawable(R.styleable.ClockView_hourHandDrawable);mMinuteHand = a.getDrawable(R.styleable.ClockView_minuteHandDrawable);mSecondHand = a.getDrawable(R.styleable.ClockView_secondHandDrawable);mDialWidth = mDial.getIntrinsicWidth();mDialHeight = mDial.getIntrinsicHeight();a.recycle();}/* * 组件attach到窗口上的回调* * @see android.view.View#onAttachedToWindow()*/@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();// 防止重复注册监听if (!mAttached) {mAttached = true;IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_TIME_TICK);filter.addAction(Intent.ACTION_TIME_CHANGED);filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);// 注册监听,监听时间变更事件getContext().registerReceiver(mIntentReceiver, filter, null,mHandler);}mCalendar = new Time();// 获取最新时间onTimeChanged();// 每隔一秒时钟变化1次post(mClockTick);}/** 组件从组件detach的回调* @see android.view.View#onDetachedFromWindow()*/@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();// 注销监听和使循环停止if (mAttached) {getContext().unregisterReceiver(mIntentReceiver);removeCallbacks(mClockTick);mAttached = false;}}/* * 在绘制时钟组件之前 (onDraw)调用,用来决定时钟组件的大小* @see android.view.View#onMeasure(int, int)*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);float hScale = 1.0f;float vScale = 1.0f;// 设置的组件宽度如果小于时钟表盘图片的宽度,则算出缩放比率if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {hScale = (float) widthSize / (float) mDialWidth;}// 同上,此处为高度if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) {vScale = (float) heightSize / (float) mDialHeight;}// 保证组件的宽高一致float scale = Math.min(hScale, vScale);setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec),resolveSize((int) (mDialHeight * scale), heightMeasureSpec));}/* * 组件大小发生变化时的回调,比如在外部设置组件的宽高* * @see android.view.View#onSizeChanged(int, int, int, int)*/@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mChanged = true;}/* * 组件绘制回调* @see android.view.View#onDraw(android.graphics.Canvas)*/@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG| Paint.FILTER_BITMAP_FLAG)); // 防锯齿boolean changed = mChanged;if (changed) {mChanged = false;}final Drawable dial = mDial;int w = dial.getIntrinsicWidth();int h = dial.getIntrinsicHeight();int availableWidth = getWidth();int availableHeight = h;int x = availableWidth / 2;int y = availableHeight / 2;boolean scaled = false;// 如有需要,等比例缩放,保证宽高一致if (availableWidth < w || availableHeight < h) {scaled = true;float scale = Math.min((float) availableWidth / (float) w,(float) availableHeight / (float) h);canvas.save();canvas.scale(scale, scale, x, y);}if (changed) {dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));}dial.draw(canvas); // 绘制表盘if (!mNoSeconds) {drawHand(canvas, mSecondHand, x, y, mSeconds / 60.0f * 360.0f,changed); // 绘制秒针}drawHand(canvas, mMinuteHand, x, y, mMinutes / 60.0f * 360.0f, changed); // 绘制分针drawHand(canvas, mHourHand, x, y, mHour / 12.0f * 360.0f, changed); // 绘制时针if (scaled) {canvas.restore();}}/*** 绘制时分秒针* * @param canvas* @param hand* @param x* @param y* @param angle* @param changed*/private void drawHand(Canvas canvas, Drawable hand, int x, int y,float angle, boolean changed) {canvas.save();canvas.rotate(angle, x, y);if (changed) {final int w = hand.getIntrinsicWidth();final int h = hand.getIntrinsicHeight();hand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));}hand.draw(canvas);canvas.restore();}/*** 时间变更回调,计算出当前的时分秒*/private void onTimeChanged() {mCalendar.setToNow();if (mTimeZoneOffset != 0){long offset = - mCalendar.gmtoff * 1000 + (mTimeZoneOffset * 3600000);if (offset != 0){mCalendar.set(mCalendar.toMillis(false) + offset);}}//     if (mTimeZoneId != null) {
//          mCalendar.switchTimezone(mTimeZoneId);
//      }int hour = mCalendar.hour;int minute = mCalendar.minute;int second = mCalendar.second;// long millis = System.currentTimeMillis() % 1000;mSeconds = second;// (float) ((second * 1000 + millis) / 166.666);mMinutes = minute + second / 60.0f;mHour = hour + mMinutes / 60.0f;mChanged = true;updateContentDescription(mCalendar);}/*** 时间变化Receiver,回调后不做逻辑处理,因为使用了mClockTick循环*/private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {// String tz = intent.getStringExtra("time-zone");// mCalendar = new Time(TimeZone.getTimeZone(tz).getID());// }// onTimeChanged();// invalidate();}};/*** 时间间隔1s循环1次*/private final Runnable mClockTick = new Runnable() {@Overridepublic void run() {onTimeChanged();invalidate();ClockView.this.postDelayed(mClockTick, 1000);}};private void updateContentDescription(Time time) {final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR;String contentDescription = DateUtils.formatDateTime(mContext,time.toMillis(false), flags);setContentDescription(contentDescription);}//   public void setTimeZone(String id) {
//      mTimeZoneId = id;
//      onTimeChanged();
//  }/*** 时差,小时为单位* * @param timezoneOffset*/public void setTimezoneOffset (int timezoneOffset) {this.mTimeZoneOffset = timezoneOffset;}/*** 设置是否需要秒针* * @param enable*/public void enableSeconds(boolean enable) {mNoSeconds = !enable;}
}

android自定义ClockView相关推荐

  1. Android自定义View绘制闹钟

    Android自定义View绘制闹钟 本文简单实现了一个闹钟,扩展View,Canvas绘制 效果如下: 代码如下: package com.gaofeng.mobile.clock_demo;imp ...

  2. Android 自定义View 时钟效果

    Android 自定义时钟浅析 最有趣,最好玩的东西,一定是高度灵活的东西,今天我们以自定义View的方式来实现一个表盘样式的时钟,并自动设置为当前时间,因为感觉网上的直接能运行的代码很少,思路也很麻 ...

  3. Android自定义ViewGroup基本步骤

    1.自定义属性,获取自定义属性,可参考 ​ Android自定义View基本步骤 ​ 2.onMeasure() 方法,for循环测量子View,根据子View的宽高来计算自己的宽 高 3.onDra ...

  4. Android自定义View —— TypedArray

    在上一篇中Android 自定义View Canvas -- Bitmap写到了TypedArray 这个属性 下面也简单的说一下TypedArray的使用 TypedArray 的作用: 用于从该结 ...

  5. Android 自定义View —— Canvas

    上一篇在android 自定义view Paint 里面 说了几种常见的Point 属性 绘制图形的时候下面总有一个canvas ,Canvas 是是画布 上面可以绘制点,线,正方形,圆,等等,需要和 ...

  6. android 自定义loading,Android自定义动画-StarLoadingView

    今天来分享第二个自定义loading的动画,起了个名字叫 蹦跶的星星 ,还是老规矩先介绍,后上图. 实现效果在最后,GIF有点大,手机流量慎重. 介绍 首先声明做这个动画的初衷是为了学习和分享,所以从 ...

  7. android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...

    转载:http://blog.csdn.net/xiabing082/article/details/48781489 1.  大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...

  8. android 自定义命名空间,Android自定义ActionBar实例

    本文实例讲述了android自定义actionbar的实现方法.分享给大家供大家参考.具体实现方法如下: android 3.0及以上已经有了actionbar的api,可以通过引入support p ...

  9. Android自定义View:ViewGroup(三)

    自定义ViewGroup本质是什么? 自定义ViewGroup本质上就干一件事--layout. layout 我们知道ViewGroup是一个组合View,它与普通的基本View(只要不是ViewG ...

  10. Android自定义视图四:定制onMeasure强制显示为方形

    这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...

最新文章

  1. 第13章:项目合同管理--章节真题
  2. 八大编程知名编程语言或系统的发展简史
  3. mysql反向生成uml类图_UML类图自动生成,太爽了
  4. 搜索引擎优化学习原理_如何使用数据科学原理来改善您的搜索引擎优化工作
  5. 7-19 谁先倒 (15 分)
  6. .NET CORE+CENTOS部署+supervisor守护
  7. SQL Sever — 上课笔记【主键、外键、唯一性约束、检查约束】
  8. Redis分布式锁零散知识
  9. java随机数种子_使用种子的Java随机数
  10. 最好的3个Windows电脑上的azw3阅读器
  11. swust oj 1069
  12. 链路聚合Eth-Trunk
  13. Cobalt Strike Malleable C2
  14. 矩阵树定理--luoguP4208 [JSOI2008]最小生成树计数
  15. 如何修改Linux服务器时间
  16. Linux双硬盘引导,Linux系统下双硬盘多系统引导深入探究
  17. 猜数字小游戏(Python)
  18. 使用Aurora在Word中插入算法伪代码教程,亲测有效,写论文必备
  19. 解决二义性问题解决 java_C++中常见的两种二义性问题及其解决方式
  20. phpcms 点赞_php+js实现点赞功能的示例详解

热门文章

  1. Git:git commit后撤销commit 提交
  2. 总比特率是什么意思_新基金初始净值低于1是什么意思?什么时候开始有净值?...
  3. 小白Linux入门之:常用命令介绍
  4. CVPR 2020-FaceShifter:能够应对脸部遮挡的高保真换脸方法
  5. karto探秘之open_karto 第三章 --- 扫描匹配
  6. HDU-ACM-2041
  7. windows环境下安装TensorFlow(亲测)
  8. python 正则表达式简介
  9. Ortholab has been moved to Google Code
  10. Vue2.0源码阅读笔记(三):计算属性