自定义控件的实现
来源:互联网 发布:工业设计软件分类 编辑:程序博客网 时间:2024/05/19 20:57
本来应该继续写Spring的学习技术博客的,今天下午帮助同学简单写了一个自定义控件,自己也熟悉回忆了一下自定义控件的绘制流程。
首先有这么一个需求,要制作一块精致华丽的挂表,怎么实现呢?
我们来分析:
1.表是圆的,是不是要先画一个圆出来呢。
2.表是有刻度的,那么怎么给这个圆上面画出刻度来呢,其实也很简单,我们先给12点画一个刻度,出来,在设置或测量出控件的大小来后,通过getWidth()可以获取空间的宽度,这样就能得到12点的X坐标getWidth()/2,12点在整个控件的顶部,他的Y坐标显而易见是0,这样通过canvas.drawLine(),方法轻易就能画出刻度来。
3.接下来我们再来分析,按小时总共有12小时,那就是,每两个时间中间的角度是360/12=30°,这就好办了,每画完一次刻度,就让画布旋转30就好了。这样就可以画出刻度来(分秒的刻度道理一样的);
4.在接下来就是如何绘制,时分秒的指针了这个更简单,起始点都是这个圆表盘的圆心,画出不同的直线来。
5.最后就是让时分秒跟着时间动态走了,其实也很简单,就是不断重回时分秒之间就可以了,invalidate();调用这个方法;
就上面这五步就基本实现这个需求了,是不是很简单,下面就把代码贴上来,供大家鉴赏,我会在代码中加上注释的:
package com.fly.customview.customclock.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.icu.util.Calendar;import android.text.format.Time;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.util.Log;import android.view.View;/** * 类名: * 类描述: * 创建人:fei.wang * 创建日期: 2017/2/2. * 版本:V1.0 * */public class ClockView extends View { private static final String TAG = ClockView.class.getSimpleName(); /**创建画圆的画笔*/ private Paint circlePaint; /**创建画圆的画笔颜色*/ private int circleColor = Color.parseColor("#dddd89"); /**创建画刻度的画笔*/ private Paint scalePaint; /**创建画刻度的画笔颜色*/ private int scaleColor = Color.parseColor("#000000"); /**创建画时、分、秒的画笔*/ private Paint mPaint; /**距离圆内部边缘的距离*/ private int padding = 5; public ClockView(Context context) { super(context); initCirclePaint(); } public ClockView(Context context, AttributeSet attrs) { super(context, attrs); initCirclePaint(); } public ClockView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initCirclePaint(); } private void initCirclePaint() { circlePaint = new Paint(); circlePaint.setColor(circleColor); circlePaint.setStyle(Paint.Style.STROKE); // 设置画笔的样式,STROKE为空心,FILL为实心 circlePaint.setStrokeWidth(5); // 设置空心的边框宽度 circlePaint.setAntiAlias(true); // 设置画笔无锯齿 scalePaint = new Paint(); scalePaint.setStyle(Paint.Style.FILL); scalePaint.setColor(scaleColor); scalePaint.setStrokeWidth(5); scalePaint.setAntiAlias(true); mPaint = new Paint(); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); } //--------------------------------- 接下来开始最重要的三个方法啦 ---------------------------------------------------- /** * 对这个方法的详细了解http://www.jianshu.com/p/ba2e73899cc7 * @param widthMeasureSpec 从父控件传递过来的宽度(包括模式和大小) * @param heightMeasureSpec 从父控件传递过来的高度(包括模式和大小) * 通过这个方法可以获取该控件的宽(getMeasuredWidth)高(getMeasuredHeight) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 把要设置控件的宽高告诉父控件 setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec)); } /** * * @param measureSpec 从父控件传递过来的 * @return 最终告诉父控件子控件需要的宽度 */ private int measureWidth(int measureSpec){ int result = 0; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); switch (mode){ case MeasureSpec.EXACTLY: // 表示父控件已经把大小限制死了 result = size; break; case MeasureSpec.AT_MOST: // 表示父控件允许子控件设置大小,但是设置的大小一定要在父控件的范围之内 result = getWidth()/2; break; case MeasureSpec.UNSPECIFIED: // 随意设,没限制,但是不管你设置多少(值大于父控件了已经),最后展示出来的,都是浮空间的大小 result = 200; break; } return result; } /** * * @param measureSpec 从父控件传递过来的 * @return 最终告诉父控件子控件需要的宽度 */ private int measureHeight(int measureSpec){ int result = 0; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); switch (mode){ case MeasureSpec.EXACTLY: // 表示父控件已经把大小限制死了 result = size; Log.e(TAG,"************EXACTLY*************"); break; case MeasureSpec.AT_MOST: // 表示父控件允许子控件设置大小,但是设置的大小一定要在父控件的范围之内 result = getHeight()/4; Log.e(TAG,"************AT_MOST*************"); break; case MeasureSpec.UNSPECIFIED: // 随意设,没限制,但是不管你设置多少(值大于父控件了已经),最后展示出来的,都是浮空间的大小 result = 200; Log.e(TAG,"************UNSPECIFIED*************"); break; } return result; } //--------------------------------- 好了经过上面的测量已经知道所绘控件的大小了,接下来开始固定控件所在的位置 --------------------------------- /** * 这个方法的详细讲解http://blog.csdn.net/liangde123/article/details/51839188 * @param changed * @param left 控件左边距离父控件左边的距离 * @param top 控件顶部距离父控件顶部的距离 * @param right 控件右边距离父控件左边的距离 * @param bottom 控件底部距离父控件顶部的距离 * 通过这个方法,可以最终获取在这个位置上控件的宽(getWidth)高(getHeight) */ @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // 把控件要展示到父控件的位置传递给父控件// layout(50,50,getMeasuredWidth() + 50,getMeasuredHeight() + 50); } /**获取屏幕的宽高*/ private void getScreenSize(){ DisplayMetrics metrics = new DisplayMetrics(); int widthPixels = metrics.widthPixels; int heightPixels = metrics.heightPixels; } //---------------------------------接下来是重头戏OnDraw()方法的实现------------------------------------------------- /** * 通过这个方法可以画出我们所需要的控件 * @param canvas 画布 */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawCircle(canvas); drawScale(canvas); drawPointer(canvas); } /**画出圆*/ private void drawCircle(Canvas canvas){ // 分别是X轴坐标,Y轴坐标,半径,画笔 canvas.drawCircle(getWidth()/2,getHeight()/2,getHeight()/2 - padding,circlePaint); } /**画出刻度*/ private void drawScale(Canvas canvas){ /** * startX:起始端点的X坐标。 * startY:起始端点的Y坐标。 * stopX:终止端点的X坐标。 * stopY:终止端点的Y坐标。 * paint:绘制直线所使用的画笔。 * * 至于为什么要这么设置坐标: * 控件的宽度getWidth()除以2就可以获取到12点的X坐标, * 因为限定(到这一步知道)了控件的高度,12点的Y坐标本身应该为0,但是上面画圆的时候画笔的宽度为5,和padding是一样的 * 并且画的时候把画笔的宽度减去了,所以这里设置是padding,是让从远的外边距算起,有点绕,需要理解 */// canvas.drawLine(getWidth()/2,padding,getWidth()/2,padding + 14,scalePaint); // 这一步只是为了演示划出的刻度 /** * 既然可以画出刻度,那么接下来我们又要分析, 表的刻度(这里只是计算是真的刻度,分针秒针原理一样的) * 时针的刻度在一个表盘里有12个,并且3、6、9、12点的时候刻度会略长 * 并且每画出一个刻度的时候画布应该旋转360/12的角度,继续画下一个刻度 * 好了分析清楚后,我们就开始画了 */ for (int i = 0; i < 12; i++) { if (i%3 == 0) { // 可以获取到3的整数倍的点(3、6、9、12) canvas.drawLine(getWidth()/2,padding,getWidth()/2,padding + 20,scalePaint); }else{ canvas.drawLine(getWidth()/2,padding,getWidth()/2,padding + 14,scalePaint); } /** * degrees 旋转的角度 * px 以某个点来旋转的点的x坐标 * py 以某个点来旋转的点的y坐标 */ canvas.rotate(30,getWidth()/2,getHeight()/2); } } /** * 接下来开始绘制时分秒的指针 * canvas.save();与canvas.restore();的详细参考下面的链接 * http://www.cnblogs.com/xirihanlin/archive/2009/07/24/1530246.html * @param canvas 画布 * 分析: * 时分秒的指针都是直线canvas.drawLine(); * 时的指针旋转角度为 360/12 * 分的指针旋转的角度 360/120 * 秒的指针旋转的角度 360/1200 */ private void drawPointer(Canvas canvas){ Time t=new Time(); // or Time t=new Time("GMT+8"); 加上Time Zone资料。 t.setToNow(); // 取得系统时间。 int hour = 1; // 0-23 if (t.hour > 12) { hour = t.hour - 12; }else{ hour = t.hour; } int minute = t.minute; int second = t.second; // 旋转的角度 float degrees = hour*30; mPaint.setColor(Color.BLACK); mPaint.setStrokeWidth(5); canvas.save(); canvas.rotate(degrees,getWidth()/2,getHeight()/2); canvas.drawLine(getWidth()/2,getHeight()/2,getWidth()/2,getHeight()/2 - 90,mPaint); canvas.restore(); // 分 degrees = minute*3; mPaint.setColor(Color.BLUE); mPaint.setStrokeWidth(4); canvas.save(); canvas.rotate(degrees,getWidth()/2,getHeight()/2); canvas.drawLine(getWidth()/2,getHeight()/2,getWidth()/2,getHeight()/2 - 70,mPaint); canvas.restore(); // 分 degrees = (float) (minute*0.3); mPaint.setColor(Color.BLUE); mPaint.setStrokeWidth(3); canvas.save(); canvas.rotate(degrees,getWidth()/2,getHeight()/2); canvas.drawLine(getWidth()/2,getHeight()/2,getWidth()/2,getHeight()/2 - 40,mPaint); canvas.restore(); invalidate();// view的刷新操作,重绘 }}
到此为止,是不是很简单,自定义控件其实也没这么神奇,慢慢也变成基础 了。
0 0
- 自定义控件的实现
- 自定义控件的实现
- 自定义控件的实现
- Android.自定义控件的实现
- Android.自定义控件的实现
- Android自定义控件的实现
- Android自定义控件的实现
- C#自定义控件的实现
- 自定义控件 BUTTON的实现。
- Android.自定义控件的实现
- 字母表自定义控件的实现
- C# 自定义控件的实现
- Android自定义控件的实现
- 自定义控件-轮播图的实现
- 复杂自定义控件---自定义ViewPager的实现
- 自定义控件点滴1(自定义控件的简单实现)
- 【VC】实现CWnd类的自定义,并实现自定义控件!
- GridView控件自定义分页的实现
- Linux 乌班图系统安装并配置nodejs
- 2017.02.28 list,set,map关系,区别细分析
- 域名注册与备案
- TCP的三握四挥
- matplotlib绘图可视化知识点整理(一)
- 自定义控件的实现
- RecycleView的刷新加强版
- python/正则匹配使用中遇到的问题
- 腾讯云服务器安装wordpress以及搭建mysql
- SSH和SSM对比总结
- 安卓源码项目进行gradle编译改造常见问题解决
- 51nod 1116 K进制下的大数
- Java学习之工厂模式
- Android启动模式内容回看