Android自定义View制作仪表盘界面

前言

最近我跟自定义View杠上了,甚至说有点上瘾到走火入魔了。身为菜鸟的我自然要查阅大量的资料,学习大神们的代码,这不,前两天正好在郭神在微信公众号里推送一片自定义控件的文章——一步步实现精美的钟表界面。正适合我这种菜鸟来学习,闲着没事,我就差不多依葫芦画瓢也写了一个自定义表盘View,现在纯粹最为笔记记录下来。先展示下效果图:

下面进入正题

自定义表盘属性

老规矩,先在attrs文件里添加表盘自定义属性

<declare-styleable name="WatchView">
<attr name="watchRadius" format="dimension"/> //表盘半径
<attr name="watchPadding" format="dimension"/> //表盘相对控件边框距离
<attr name="watchScalePadding" format="dimension"/> //刻度相对表盘距离
<attr name="watchScaleColor" format="color|reference"/> //常规刻度颜色
<attr name="watchScaleLength" format="dimension|reference"/> //常规刻度长度
<attr name="watchHourScaleColor" format="dimension|reference"/> //整点刻度颜色
<attr name="watchHourScaleLength" format="dimension|reference"/> //整点刻度长度
<attr name="hourPointColor" format="color|reference"/> //时针颜色
<attr name="hourPointLength" format="dimension|reference"/> //时针长度
<attr name="minutePointColor" format="color|reference"/> //分针颜色
<attr name="minutePointLength" format="dimension|reference"/> //分针长度
<attr name="secondPointColor" format="color|reference"/> //秒针颜色
<attr name="secondPointLength" format="dimension|reference"/> //秒针长度
<attr name="timeTextSize" format="dimension|reference"/> //表盘字体大小
<attr name="timeTextColor" format="color|reference"/> //表盘字体颜色
</declare-styleable>

在自定义View的构造方法种获取自定义属性

先将属性变量声明如下:

<span style="font-size:14px;"> /**表盘边距*/
private float mWatchPadding = 5;
/**表盘与刻度边距*/
private float mWatchScalePadding = 5;
/**表盘半径*/
private float mWatchRadius = 250;
/**表盘刻度长度*/
private float mWatchScaleLength;
/**表盘刻度颜色*/
private int mWatchScaleColor = Color.BLACK;
/**表盘整点刻度长度*/
private float mHourScaleLength = 8;
/**表盘整点刻度颜色*/
private int mHourScaleColor = Color.BLUE;
/**表盘时针颜色*/
private int mHourPointColor = Color.BLACK;
/**表盘时针长度*/
private float mHourPointLength = 100;
/**表盘分针颜色*/
private int mMinutePointColor = Color.BLACK;
/**表盘分针长度*/
private float mMinutePointLength = 130;
/**表盘秒针颜色*/
private int mSecondPointColor = Color.RED;
/**表盘秒针长度*/
private float mSecondPointLength = 160;
/**表盘尾部指针长度*/
private float mEndPointLength;
/**表盘数字颜色*/
private int mTimeTextColor = Color.BLACK;
/**表盘数字大小*/
private int mTimeTextSize = 15;</span>

在构造方法种获取自定义属性

<span style="font-size:14px;"> public WatchView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.WatchView);
int n = array.getIndexCount();
for (int i = 0;i<n;i++){
int attr = array.getIndex(i);
switch (attr){
case R.styleable.WatchView_watchRadius:
mWatchRadius = array.getDimensionPixelSize(attr,MyUtil.dip2px(context,60));
break;
case R.styleable.WatchView_watchPadding:
mWatchPadding = array.getDimensionPixelSize(attr,MyUtil.dip2px(context,5));
break;
case R.styleable.WatchView_watchScalePadding:
mWatchScalePadding = array.getDimensionPixelSize(attr,MyUtil.dip2px(context,3));
break;
case R.styleable.WatchView_watchScaleLength:
mWatchScaleLength = array.getDimensionPixelSize(attr,MyUtil.dip2px(context,5));
break;
case R.styleable.WatchView_watchScaleColor:
mWatchScaleColor = array.getColor(attr, Color.parseColor("#50000000"));
break;
case R.styleable.WatchView_watchHourScaleLength:
mHourScaleLength = array.getDimensionPixelSize(attr,MyUtil.dip2px(context,10));
break;
case R.styleable.WatchView_watchHourScaleColor:
mHourScaleColor = array.getColor(attr,Color.BLACK);
break;
case R.styleable.WatchView_hourPointLength:
mHourPointLength = array.getDimensionPixelSize(attr,MyUtil.dip2px(context,35));
break;
case R.styleable.WatchView_hourPointColor:
mHourPointColor = array.getColor(attr,Color.BLACK);
break;
case R.styleable.WatchView_minutePointLength:
mMinutePointLength = array.getDimensionPixelSize(attr,MyUtil.dip2px(context,40));
break;
case R.styleable.WatchView_minutePointColor:
mMinutePointColor = array.getColor(attr,Color.BLACK);
break;
case R.styleable.WatchView_secondPointLength:
mSecondPointLength = array.getDimensionPixelSize(attr,MyUtil.dip2px(context,50));
break;
case R.styleable.WatchView_secondPointColor:
mSecondPointColor = array.getColor(attr,Color.BLUE);
break;
case R.styleable.WatchView_timeTextSize:
mTimeTextSize = array.getDimensionPixelSize(attr,MyUtil.dip2px(context,15));
break;
case R.styleable.WatchView_timeTextColor:
mTimeTextColor = array.getColor(attr,Color.BLACK);
break;
}
}
array.recycle();
}</span>

设置控件大小

这里当然就是重写onMeasure方法啦,这里我们处理的简单点,如下面代码所示,当我们将控件的宽高都设定为wrap_content(即MeasureSpec.UNSPECIFED)时,我们将宽高设定为默认值(wrapContentSize)和圆盘半径+圆盘边距(mWatchRadius+mWatchPadding)之间取最大值,其他情况下就取系统自取值。当然作为一个严谨的控件,仅仅这样处理肯定是不行的。项目中,我们要根据我们的需求自行修改里面的代码以适配。

<span style="font-size:14px;"> @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int wrapContentSize = 1000;
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.UNSPECIFIED && heightMode == MeasureSpec.UNSPECIFIED){
wrapContentSize = (int) Math.max(wrapContentSize,mWatchRadius+mWatchPadding);
setMeasuredDimension(wrapContentSize,wrapContentSize);
}else {
setMeasuredDimension(widthSize,heightSize);
}
}</span>

重写onDraw方法

来到最关键真正画表盘时刻了。一步一步来,首先初始化我们的画笔(我的习惯,写一个initPaint方法)

<span style="font-size:14px;"> private void initPaint(){
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
}</span>

为了不显赘述,方便理解,我直接展示代码,在代码中解释

开画之前我们先将画笔移动到控件中心点位置,如下:

<span style="font-size:14px;">@Override
protected void onDraw(Canvas canvas) {
canvas.translate(getWidth()/2,getHeight()/2);
}</span>

第一步,画表盘

<span style="font-size:14px;"> /**
* 画表盘
* @param canvas
*/
private void paintWatchBoard(Canvas canvas){
initPaint();
canvas.save();
canvas.drawCircle(0,0,mWatchRadius,mPaint); //画圆盘
canvas.restore();
}</span>

注:每次画图之前都要先调用canvas.save()方法,保存画笔属性,画完之后要调用canvas.restore()方法,重置画笔属性

这里就不一一展示每次画完之后的效果图了。

第二步,画刻度+整点时间数字(刻度从12点方向开始画)

<span style="font-size:14px;"> /**
* 画刻度及整点数字
* @param canvas
*/
private void paintScale(Canvas canvas){
int lineLength; //刻度线长度
canvas.save();
for (int i = 0;i<60;i++){
if (i%5 == 0){//整点刻度下画笔相关属性
mPaint.setStrokeWidth(MyUtil.dip2px(getContext(),1.5f));
mPaint.setColor(mHourScaleColor);
lineLength = MyUtil.dip2px(getContext(),8);
canvas.drawLine(0,-mWatchRadius+mWatchScalePadding,0,-mWatchRadius+mWatchScalePadding+lineLength,mPaint);
mPaint.setColor(mTimeTextColor);
mPaint.setTextSize(mTimeTextSize);
canvas.drawText(mTimes[i/5],-mTimeTextSize/2,-mWatchRadius+mWatchScalePadding + lineLength+mTimeTextSize,mPaint);//整点的位置标上整点时间数字
}else {//非整点刻度下画笔相关属性
mPaint.setStrokeWidth(MyUtil.dip2px(getContext(),0.8f));
mPaint.setColor(mWatchScaleColor);
lineLength = MyUtil.dip2px(getContext(),5);
canvas.drawLine(0,-mWatchRadius+mWatchScalePadding,0,-mWatchRadius+mWatchScalePadding+lineLength,mPaint);
}
canvas.rotate(6);//每次画完一个刻度线,画笔顺时针旋转6度(360/60,相邻两刻度之间的角度差为6度)
}
canvas.restore();
}</span>

其中,整点数字我用了罗马数字来表示

<span style="font-size:14px;">private String[] mTimes = {"XII","Ⅰ","Ⅱ","Ⅲ","Ⅳ","Ⅴ","Ⅵ","Ⅶ","Ⅷ","Ⅸ","Ⅹ","XI"};</span>

第三步,画时针、分针、秒针以及其它修饰图

考虑到时针、分针和秒针大小长度各不一样,我这里特意定义了三支画笔来分别画时针、分针和秒针。

同样的,先初始化指针画笔:

<span style="font-size:14px;">/**
* 初始化指针
*/
private void initPointPaint(){
mHourPaint = new Paint();
mHourPaint.setAntiAlias(true);
mHourPaint.setStyle(Paint.Style.FILL);
mHourPaint.setStrokeWidth(16);
mHourPaint.setColor(mHourPointColor);
mMinutePaint = new Paint();
mMinutePaint.set(mHourPaint);
mMinutePaint.setStrokeWidth(12);
mMinutePaint.setColor(mMinutePointColor);
mSecondPaint = new Paint();
mSecondPaint.set(mHourPaint);
mSecondPaint.setStrokeWidth(7);
mSecondPaint.setColor(mSecondPointColor);
mEndPointLength = mWatchRadius/6; //(修饰部分)指针尾部长度,定义为表盘半径的六分之一
}</span> 

画指针

<span style="font-size:14px;">/**
* 画指针
* @param canvas
*/
private void paintPoint(Canvas canvas){
initPointPaint();
Calendar c = Calendar.getInstance(); //取当前时间
int hour = c.get(Calendar.HOUR_OF_DAY);
int minute = c.get(Calendar.MINUTE);
int second = c.get(Calendar.SECOND);
//绘制时针
canvas.save();
canvas.rotate(hour*30);
canvas.drawLine(0,0,0,-mHourPointLength,mHourPaint);
canvas.drawLine(0,0,0,mEndPointLength,mHourPaint);
canvas.restore();
//绘制分针
canvas.save();
canvas.rotate(minute*6);
canvas.drawLine(0,0,0,-mMinutePointLength,mMinutePaint);
canvas.drawLine(0,0,0,mEndPointLength,mMinutePaint);
canvas.restore();
//绘制秒针
canvas.save();
canvas.rotate(second*6);
canvas.drawLine(0,0,0,-mSecondPointLength,mSecondPaint);
canvas.drawLine(0,0,0,mEndPointLength,mSecondPaint);
canvas.restore();
}</span>

OK,该有的差不多都有了,直接在onDraw中调用吧

<span style="font-size:14px;">@Override
protected void onDraw(Canvas canvas) {
canvas.translate(getWidth()/2,getHeight()/2);
paintWatchBoard(canvas); //画表盘
paintScale(canvas); //画刻度
paintPoint(canvas); //画指针
canvas.drawCircle(0,0,15,mSecondPaint); //为了美观,也让表盘更接近我们显示生活中的样子,我在圆盘中心画了一个大红圆点装饰秒针
postInvalidateDelayed(1000); //每隔一秒钟画一次
}</span>

(⊙v⊙)嗯,自定义View大功告成,我们在布局文件里调用看下效果吧

<span style="font-size:14px;"><?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:zhusp="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent">
<com.wondertek.propertyanimatordemo.WatchView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
zhusp:timeTextSize="20dp"
zhusp:watchRadius="150dp"
zhusp:hourPointLength="80dp"
zhusp:minutePointLength="100dp"
zhusp:secondPointLength="115dp"/>
</RelativeLayout></span>

最后我这里的静态效果是这样的:

(0)

相关推荐

  • Android自定义View实现拖动选择按钮

    本文为大家分享了Android实现拖动选择按钮的具体代码,供大家参考,具体内容如下 效果图 View代码 第一步:自定义属性 <declare-styleable name="DragView"> <attr name="icon_drag" format="reference"/> <attr name="color_circle" format="color"/> &

  • Android自定义View之自定义评价打分控件RatingBar实现自定义星星大小和间距

    在Android开发中,我们经常会用到对商家或者商品的评价,运用星星进行打分.然而在Android系统中自带的打分控件,RatingBar特别不好用,间距和大小无法改变.所以,我就自定义了一个特别好用的打分控件.在项目中可以直接使用,特别简单.下面直接上图: 效果图 实现原理 其实就是自定义View继承LinearLayout ,然后里面动态加了五个ImageView. 实现代码,有详细的注释 在attrs中声明的可以在xml中设置的变量 <declare-styleable name="

  • Android自定义View圆形和拖动圆、跟随手指拖动效果

    单纯的自定义一个圆非常简单 只需要几步就完成 拖动圆添加实现触摸事件即可 我在第一次自定义View圆遇到的几个Bug: 1.拖动圆的话在xml里面设置的自定义圆的宽和高是它能活动的空间的大小 不是圆控件的大小 如果你定义了100dp 拖动它的时候超过100dp这个距离这个圆就会看不见 就像下面这样 如果想活动于整个屏幕直接给宽和高match_parent属性就好了 2.我在定义充满属性match_parent的时候运行会报错,什么方法都用了就是不行,耐心等待过一会就好了-有可能是studio没来

  • Android自定义View仿支付宝输入六位密码功能

    跟选择银行卡界面类似,也是用一个PopupWindow,不过输入密码界面是一个自定义view,当输入六位密码完成后用回调在Activity中获取到输入的密码并以Toast显示密码.效果图如下: 自定义view布局效果图及代码如下: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/

  • Android自定义View制作仪表盘界面

    前言 最近我跟自定义View杠上了,甚至说有点上瘾到走火入魔了.身为菜鸟的我自然要查阅大量的资料,学习大神们的代码,这不,前两天正好在郭神在微信公众号里推送一片自定义控件的文章--一步步实现精美的钟表界面.正适合我这种菜鸟来学习,闲着没事,我就差不多依葫芦画瓢也写了一个自定义表盘View,现在纯粹最为笔记记录下来.先展示下效果图: 下面进入正题 自定义表盘属性 老规矩,先在attrs文件里添加表盘自定义属性 <declare-styleable name="WatchView"&

  • Android自定义view制作绚丽的验证码

    废话不多说了,先给大家展示下自定义view效果图,如果大家觉得还不错的话,请继续往下阅读. 怎么样,这种验证码是不是很常见呢,下面我们就自己动手实现这种效果,自己动手,丰衣足食,哈哈~ 一. 自定义view的步骤 自定义view一直被认为android进阶通向高手的必经之路,其实自定义view好简单,自定义view真正难的是如何绘制出高难度的图形,这需要有好的数学功底(后悔没有好好学数学了~),因为绘制图形经常要计算坐标点及类似的几何变换等等.自定义view通常只需要以下几个步骤: 写一个类继承

  • Android自定义view制作抽奖转盘

    本文实例为大家分享了Android自定义view制作抽奖转盘的具体代码,供大家参考,具体内容如下 效果图 TurntableActivity package com.bawei.myapplication.turntable; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; im

  • android自定义view制作圆形进度条效果

    还是我们自定View的那几个步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 [ 3.重写onMesure ] 4.重写onDraw 1.自定义属性: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomTitleView"> <attr name="m

  • Android自定义View制作动态炫酷按钮实例解析

    普通按钮也就那么几种样式,看着都审美疲劳,先放效果图: 你会不会以为这个按钮是集结了很多动画的产物,我告诉你,并没有.所有的实现都是基于自定义View,采用最底层的onDraw一点一点的画出来的.没有采用一丁点的动画.虽然演示时间很短,但是要完成这么多变化,还是挺吃力. 首先讲解用法: public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState)

  • Android自定义View实现渐变色仪表盘

    前言:最近一直在学自定义View的相关知识,感觉这在Android中还是挺难的一块,当然这也是每个程序员必经之路,正好公司项目要求实现类似仪表盘的效果用于直观的显示公司数据,于是就简单的写了个demo,记录实现的过程.上篇<Android自定义View实现圆弧进度效果>简单记录了圆弧及文字的绘制,渐变色的仪表盘效果将更加升入的介绍canvas及paint的使用(如画布旋转,paint的渐变色设置等). 知识梳理 1.圆弧渐变色(SweepGradient) 2.圆弧上刻度绘制 3.指针指示当前

  • Android自定义view实现雪花特效实例代码

    目录 一.前言 二.创意名 三.效果展示 四.实现步骤 五.编码实现 总结 一.前言 这个冬天,老家一直没有下雨, 正好圣诞节,就想着制作一个下雪的特效. 圣诞祝福:平安夜,舞翩阡.雪花飘,飞满天.心与心,永相伴. 圣诞节是传统的宗教节日,对于基 督徒,那是庆祝耶稣的诞生,纪念耶稣和发扬基督精神.现在整个西方社会都在过圣诞节,像许多宗教节日一样,它已经越来越民俗化了. 尽管如此,圣诞节依然倍受尊重.人们在圣诞快乐中怀有对耶稣的敬仰,欢乐的节庆里含有庄严肃穆的神念.欢度圣诞佳节的人都不拒绝耶稣的教

  • Android自定义View绘制随机生成图片验证码

    本篇文章讲的是Android自定义View之随机生成图片验证码,开发中我们会经常需要随机生成图片验证码,但是这个是其次,主要还是想总结一些自定义View的开发过程以及一些需要注意的地方. 按照惯例先看看效果图: 一.先总结下自定义View的步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 3.重写onMesure 4.重写onDraw 其中onMesure方法不一定要重写,但大部分情况下还是需要重写的 二.View 的几个构造函数 1.public CustomV

  • Android 自定义view模板并实现点击事件的回调

    Android 自定义view模板并实现点击事件的回调 主要的目的就是仿老版QQ的一个界面做一个模板.然后实现点击事件的回调.先看效果图: 步骤如下: 1.在res/values/目录下新建一个atts.xml文件 内容如下: <resources> <declare-styleable name="topbar"> <attr name="title" format="string"/> <attr n

  • Android自定义View实现带数字的进度条实例代码

    第一步.效果展示 图1.蓝色的进度条 图2.红色的进度条 图3.多条颜色不同的进度条 图4.多条颜色不同的进度条 第二步.自定义ProgressBar实现带数字的进度条 0.项目结构 如上图所示:library项目为自定义的带数字的进度条NumberProgressBar的具体实现,demo项目为示例项目以工程依赖的方式引用library项目,然后使用自定义的带数字的进度条NumberProgressBar来做展示 如上图所示:自定义的带数字的进度条的library项目的结构图 如上图所示:de

随机推荐