热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

这篇文章主要介绍了Android自定义View制作仪表盘界面的相关资料,首先需要自定义仪表盘的属性,在构造方法种获取自定义属性,本文介绍的非常详细,具有参考借鉴价值,需要的朋友可以参考下

前言

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

下面进入正题

自定义表盘属性

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

 
 //表盘半径 
 //表盘相对控件边框距离 
 //刻度相对表盘距离 
 //常规刻度颜色 
 //常规刻度长度 
 //整点刻度颜色 
 //整点刻度长度 
 //时针颜色 
 //时针长度 
 //分针颜色 
 //分针长度 
 //秒针颜色 
 //秒针长度 
 //表盘字体大小 
 //表盘字体颜色 

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

先将属性变量声明如下:

 /**表盘边距*/ 
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;

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

 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

设置控件大小

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

 @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); 
} 
}

重写onDraw方法

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

 private void initPaint(){ 
mPaint = new Paint(); 
mPaint.setAntiAlias(true); 
mPaint.setColor(Color.WHITE); 
mPaint.setStyle(Paint.Style.FILL); 
}

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

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

@Override 
protected void onDraw(Canvas canvas) { 
canvas.translate(getWidth()/2,getHeight()/2); 
}

第一步,画表盘

 /** 
* 画表盘 
* @param canvas 
*/ 
private void paintWatchBoard(Canvas canvas){ 
initPaint(); 
canvas.save(); 
canvas.drawCircle(0,0,mWatchRadius,mPaint); //画圆盘 
canvas.restore(); 
}

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

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

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

 /** 
* 画刻度及整点数字 
* @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(); 
}

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

private String[] mTimes = {"XII","Ⅰ","Ⅱ","Ⅲ","Ⅳ","Ⅴ","Ⅵ","Ⅶ","Ⅷ","Ⅸ","Ⅹ","XI"};

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

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

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

/** 
* 初始化指针 
*/ 
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; //(修饰部分)指针尾部长度,定义为表盘半径的六分之一 
} 

画指针

/** 
* 画指针 
* @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(); 
}

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

@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); //每隔一秒钟画一次 
}

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

<&#63;xml version="1.0" encoding="utf-8"&#63;> 
 
 

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


推荐阅读
  • 国内BI工具迎战国际巨头Tableau,稳步崛起
    尽管商业智能(BI)工具在中国的普及程度尚不及国际市场,但近年来,随着本土企业的持续创新和市场推广,国内主流BI工具正逐渐崭露头角。面对国际品牌如Tableau的强大竞争,国内BI工具通过不断优化产品和技术,赢得了越来越多用户的认可。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 解决微信电脑版无法刷朋友圈问题:使用安卓远程投屏方案
    在工作期间想要浏览微信和朋友圈却不太方便?虽然微信电脑版目前不支持直接刷朋友圈,但通过远程投屏技术,可以轻松实现在电脑上操作安卓设备的功能。 ... [详细]
  • SQLite 动态创建多个表的需求在网络上有不少讨论,但很少有详细的解决方案。本文将介绍如何在 Qt 环境中使用 QString 类轻松实现 SQLite 表的动态创建,并提供详细的步骤和示例代码。 ... [详细]
  • 解决JAX-WS动态客户端工厂弃用问题并迁移到XFire
    在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ... [详细]
  • 本文详细介绍如何使用arm-eabi-gdb调试Android平台上的C/C++程序。通过具体步骤和实用技巧,帮助开发者更高效地进行调试工作。 ... [详细]
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 数据管理权威指南:《DAMA-DMBOK2 数据管理知识体系》
    本书提供了全面的数据管理职能、术语和最佳实践方法的标准行业解释,构建了数据管理的总体框架,为数据管理的发展奠定了坚实的理论基础。适合各类数据管理专业人士和相关领域的从业人员。 ... [详细]
author-avatar
j相知相守相爱
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有