Android自定义View时钟效果
来源:互联网 发布:php get 参数隐藏 编辑:程序博客网 时间:2024/05/16 08:20
今天继续聊自定义View,当然今天的这个比较麻烦一些,如果没有自定义View的经历,建议先看看自定义文字View与水印图片View
自定义文字View
自定义水印图片View
前面的自定义文字View,图片View都属于比较简单的自定义View,今天玩点有难度的,当然目的也是为了更加熟悉自定义View的各个步骤与坐标的计算、画笔的各种属性等。话不多聊 ,我们今天实现下如下的效果:
首先进行简单的分析:
钟表盘构成属性如下:
1、外部圆形边框(宽度、颜色)
2、内部一周的小黑点(宽度、颜色)
3、内部的1-12数字(字号、颜色)
4、时针分针和秒针(规格、颜色)
简单分析之后就能确定我们需要哪些的属性值,当然此处全部自定义属性,有些事没有必要的。
分析之后进行属性的自定义:
属性的自定义如下:
<declare-styleable name="DemoClockView01"> <attr name="borderwidth" format="dimension"/> <attr name="bordercolor" format="color"/> <attr name="pointcolor" format="color"/> <attr name="hourcolor" format="color"/> <attr name="minutecolor" format="color"/> <attr name="secondcolor" format="color"/> <attr name="numsize" format="dimension"/> <attr name="numcolor" format="color"/> </declare-styleable>其中的borderwidth和bordercolor为边框的宽度和颜色,pointcolor为一圈的小黑点的颜色,hourcolor、minutecolor和secondcolor为时针、分针和秒针的颜色,numsize和numcolor为一圈数字的字号大小和颜色。
属性自定义完成之后再Java代码中拿到相关的属性值:
代码如下:
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DemoClockView01, defStyleAttr, 0); for (int i = 0; i < array.getIndexCount(); i++) {//用getIndexCount 减少循环次数,提高性能 用.length也不能执行所有的case情况 int attr = array.getIndex(i); switch (attr) { case R.styleable.DemoClockView01_borderwidth://边框宽度 mBorderWidth = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics())); break; case R.styleable.DemoClockView01_bordercolor://边框颜色 mBorderColor = array.getColor(attr, Color.BLACK); break; case R.styleable.DemoClockView01_numcolor://数字颜色 mNumColor = array.getColor(attr, Color.BLACK); break; case R.styleable.DemoClockView01_numsize://数字字号 mNumSize = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics())); break; case R.styleable.DemoClockView01_pointcolor://周围小点颜色 mPointColor = array.getColor(attr, Color.BLACK); break; case R.styleable.DemoClockView01_hourcolor://时针颜色 mHourColor = array.getColor(attr, Color.BLACK); break; case R.styleable.DemoClockView01_minutecolor://分针颜色 mMinuteColor = array.getColor(attr, Color.BLACK); break; case R.styleable.DemoClockView01_secondcolor://秒针颜色 mSecondColor = array.getColor(attr, Color.BLACK); break; } } array.recycle();拿到相关的属性之后进行控件宽和高的测量:
代码如下:
@Override protected 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); if (widthMode == MeasureSpec.EXACTLY) { mWidth = widthSize; } else { int desire = getPaddingLeft() + getPaddingRight() + (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics()); mWidth = Math.min(desire, widthSize); } if (heightMode == MeasureSpec.EXACTLY) { mHeight = heightSize; } else { int desire = getPaddingTop() + getPaddingBottom() + (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics()); mHeight = Math.min(desire, heightSize); } mWidth = Math.min(mWidth, mHeight);//取最小值 防止绘制内容出错 以最小的边来为基准进行相关的绘制 setMeasuredDimension(mWidth, mWidth); }测量完成之后进行最后的绘制,从外到内一个一个来绘制:
1、首先是外部边框,圆形,计算圆心及半径,绘制如下:
/** * 圆心的xy和圆环的宽度 */ final int cx, cy, width; cx = getPaddingLeft() + (getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) / 2; cy = getPaddingTop() + (getMeasuredHeight() - getPaddingTop() - getPaddingBottom()) / 2; width = Math.min(getWidth() / 2, getHeight() / 2);//半径 mPaint.setAntiAlias(true);//去除边缘锯齿,优化绘制效果 mPaint.setColor(mBorderColor); if (mBorderColor == 0){ mPaint.setColor(Color.BLACK); } canvas.drawCircle(cx, cy, width, mPaint);//外圆 红色 mPaint.setColor(Color.WHITE); canvas.drawCircle(cx, cy, width - mBorderWidth, mPaint);//内圆 白色此步骤完成之后在布局文件饮用控件即可看到外部的圆环效果,如下图:
2、周围小黑点的绘制:
mPaint.setColor(mPointColor); if (mPointColor == 0) { mPaint.setColor(Color.BLACK); } canvas.save();//保存当前的状态 for (int i = 0; i < 60; i++) {//总共60个点 所以绘制60次 //绘制一圈的小黑点 if (i % 5 == 0) { canvas.drawRect(cx - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()), getPaddingTop() + mBorderWidth + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()), cx + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()), getPaddingTop() + mBorderWidth + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getResources().getDisplayMetrics()), mPaint); } else { canvas.drawRect(cx - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()), getPaddingTop() + mBorderWidth + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()), cx + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()), getPaddingTop() + mBorderWidth + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()), mPaint); } canvas.rotate(6, cx, cy);//360度 绘制60次 每次旋转6度 } canvas.restore();//将canvas转回来此步骤绘制完成之后即可看到圆环加上小圆点的效果,效果如下图:
3、数字的绘制(此处绘制的数字可能旋转了,因为绘制的时候是旋转画布绘制的,当然也可以计算每个数字点的坐标进行相关的绘制)
mPaint.setColor(mNumColor); if (mNumColor == 0) { mPaint.setColor(Color.BLACK); } mPaint.setTextSize(mNumSize); if (mNumSize == 0) { mPaint.setTextSize((int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics())); } mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics())); String[] strs = new String[]{"12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11",};//绘制数字1-12 (数字角度不对 可以进行相关的处理) Rect rect = new Rect(); canvas.save(); for (int i = 0; i < 12; i++) {//绘制12次 每次旋转30度 mPaint.getTextBounds(strs[i], 0, strs[i].length(), rect); canvas.drawText(strs[i], cx - rect.width() / 2, getPaddingTop() + mBorderWidth + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 18, getResources().getDisplayMetrics()) + rect.height(), mPaint); canvas.rotate(30, cx, cy); } canvas.restore();此处绘制完成即可看到圆环、黑点和数字效果如下图:
4、时针、分针、秒针和圆心的绘制:
mPaint.setColor(mHourColor); if (mHourColor == 0) { mPaint.setColor(Color.BLACK); } canvas.save();//绘制时针 canvas.drawRect(cx - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()), getPaddingTop() + mBorderWidth + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics()) + rect.width(), cx + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()), cy, mPaint); canvas.restore(); mPaint.setColor(mMinuteColor); if (mMinuteColor == 0) { mPaint.setColor(Color.BLACK); } canvas.save();//保存后面的状态 canvas.rotate(60, cx, cy); canvas.drawRect(cx - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()), getPaddingTop() + mBorderWidth + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25, getResources().getDisplayMetrics()), cx + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()), cy, mPaint); canvas.restore();//撤销保存的状态 mPaint.setColor(mSecondColor); if (mSecondColor == 0) { mPaint.setColor(Color.BLACK); } canvas.save(); mPaint.setColor(Color.RED); canvas.rotate(120, cx, cy); canvas.drawRect(cx - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()), getPaddingTop() + mBorderWidth + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25, getResources().getDisplayMetrics()), cx + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()), cy, mPaint); canvas.restore(); mPaint.setColor(Color.RED); canvas.drawCircle(cx, cy, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, getResources().getDisplayMetrics()), mPaint);//圆心,红色此时完成即可看到完整的钟表盘效果。
如下图:
静态时钟功能到此实现。
下面呢的任务就是让其动起来:
1)时间任务,每隔1s绘制界面。
在构造方法中启动时间任务,如下:
Timer timer = new Timer("绘制线程"); timer.schedule(new TimerTask() { @Override public void run() { } }, 0, 1000);
2)计算每个时刻的对应的时针、分针、及秒针的角度。
在构造方法初始化时间如下(这里的时间类使用Calendar类):
mCalendar = Calendar.getInstance();
在onDraw方法中进行时间及角度的计算:
//关于当前时间的计算,默认为当前时间 当然是可以设置的 int hour = mCalendar.get(Calendar.HOUR);//HOUR 进制为12小时 HOUR_OF_DAY 为24小时 int minute = mCalendar.get(Calendar.MINUTE);//分钟 int second = mCalendar.get(Calendar.SECOND) + 1;//秒数 if (second == 60) { minute += 1; second = 0; } if (minute == 60){ hour += 1; minute = 0; } if (hour == 12){ hour = 0; } mCalendar.set(Calendar.SECOND, second); mCalendar.set(Calendar.MINUTE, minute); mCalendar.set(Calendar.HOUR, hour); float hourDegree = 360 * hour / 12 + 360 / 12 * minute / 60;//时针转动的角度 小时对应角度 加上 分钟对应角度 秒针忽略 float minuteDegree = 360 * minute / 60 + 360 / 60 * second / 60;//分针转动的角度 分针对应角度 加上 秒数对应角度 float secondDegree = 360 * second / 60;// 秒数对应角度
3)子线程、UI线程的切换绘制。
Handler进行子线程到子线程的转换。
最终的实现效果如下,也可以自己进行时间的设置:
到此效果实现。
Demo下载
git地址:https://github.com/SnowJun/OwnView
- Android 自定义View 时钟效果
- Android自定义View时钟效果
- Android自定义View实现时钟效果ClockView
- Android自定义View——实现时钟效果
- android 自定义view实现时钟
- Android自定义View---秒表/时钟
- android 自定义View模拟时钟
- 自定义View+Handle 实现模拟时钟效果
- android自定义view之自定义时钟wacthview
- Android自定义View之数字时钟
- Android 自定义View(四) 时钟clockView
- android自定义view(实现时钟显示)
- Android 自定义View之: 时钟控件
- Android自定义ClockView实现时钟效果
- Android自定义控件之ClockView时钟效果
- android自定义view锯齿状效果
- Android自定义View-刮刮卡效果
- 心电图效果View Android自定义View
- 自动装配bean【Spring 入门】
- Halcon之hough变换检测直线
- 11111
- ldap输出日志信息
- eclipse 快捷键大全,eclipse查找类
- Android自定义View时钟效果
- CoreOS 实战:剖析 etcd
- Block的详细分析(二)
- Elasticsearch查询配置(Elasticsearch+springboot)
- JavaBean在JSP中的应用
- (转帖)CentOS最常用命令及快捷键整理
- HTTP 头部解释,HTTP 头部详细分析,最全HTTP头部信息
- buildMaven
- 每日F&Q(2017.2.21)