欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-03 20:00:16
前言 最近我跟自定义view杠上了,甚至说有点上瘾到走火入魔了。身为菜鸟的我自然要查阅大量的资料,学习大神们的代码,这不,前两天正好在郭神在微信公众号里推送一片自定义控件...

前言

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

Android自定义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>

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

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