热门标签 | 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;> 
 
 

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


推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 【Windows】实现微信双开或多开的方法及步骤详解
    本文介绍了在Windows系统下实现微信双开或多开的方法,通过安装微信电脑版、复制微信程序启动路径、修改文本文件为bat文件等步骤,实现同时登录两个或多个微信的效果。相比于使用虚拟机的方法,本方法更简单易行,适用于任何电脑,并且不会消耗过多系统资源。详细步骤和原理解释请参考本文内容。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
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社区 版权所有