您的位置:首页 > 移动开发 > Android开发

[置顶] 一个钟表带你进入Android绘图的世界

2016-08-24 15:07 309 查看
前言

顾名思义,就是在Android手机屏幕中绘制我们需要的内容,根据绘制内容的大小(measure),布局(layout)来讲具体内容展示在屏幕中,通过绘制(draw)来实现我们需要的效果.

绘图原理(三部曲)

Measure

measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:

(1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width,
height)保存计算结果。

 

Layout

 layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:

     (1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;

     (2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;

 

Draw

draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:

     (1)绘制背景;

     (2)如果要视图显示渐变框,这里会做一些准备工作;

     (3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;

     (4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;

     (5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;

     (6)绘制滚动条;

      从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。

 

参考:http://blog.csdn.net/xu_fu/article/details/7829721                                     

分类

2D绘图

基于Android SDK内部自己提供,也是我们学习的主要内容,2D绘图的api大部分是android.graphics和android.graphics.drawable包中,他们提供了Canvas,ColorFilter,Point和RetcF等,以及一些动画相关的:AnimationDrawable,BitmapDrawable和TransitionDrawable等.

3D绘图

用Open GL ES 1.0

 

参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html              

 

2D绘图

内容

通过canvas对象可以绘制的[弧线],[Bitmap],[圆],[点],[矩形],[圆角矩形],[文本],[路径]等图形,其次还提供了旋转(rotate),缩放(scale),渐变(translate)和扭曲(skew)等转换方法.

正文

今天就以2D我学习例子,来完成对钟表的绘制实现.

知识点

1.Paint的创建于使用
2.Canvas的使用
3.Handler使用
4.onMeasure使用
5.Canvas.restore()和Canvas.save的使用

效果图:




实现步骤:

1.准备工作,初始化画笔工具,初始化时间,初始化圆环半径
2.画圆环
3.画刻度
4.移动坐标中心到画布中心
5.画指针(画时针,画分针,画秒针)
6.画时间文字

源码解析

第一步|准备工作

private Paint mPaintRing;// 圆环画笔
private Paint mPaintDegree;// 刻度/圆心画笔
private Paint mPaintText;// 文字画笔
private float strokeWidthText = 2;// 文字画笔厚度
private float strokeWidthRing = 4;// 圆环画笔厚度
private float radius = 0;// 圆环半径

//用于初始化时间的角度设置
private float hourDegree = 0;// 时针角度
private float minuteDegree = 0;// 分针角度
private float secondDegree = 0;// 秒针角度

private Date mCurrentDate=null;// 用户设置时间,默认为当前时间

private void initPaint() {
mCurrentDate=new Date();
setDate(mCurrentDate);
// 初始化圆环画笔
mPaintRing = new Paint();
mPaintRing.setColor(Color.RED);
mPaintRing.setStyle(Paint.Style.STROKE);
mPaintRing.setStrokeWidth(strokeWidthRing);
mPaintRing.setAntiAlias(true);
// 初始化刻度画笔
mPaintDegree = new Paint();
mPaintDegree.setColor(Color.RED);
mPaintDegree.setStyle(Paint.Style.FILL);
mPaintDegree.setAntiAlias(true);
// 初始化文字画笔
mPaintText = new Paint();
mPaintText.setColor(Color.RED);
mPaintText.setStyle(Paint.Style.FILL);
mPaintText.setAntiAlias(true);
mPaintText.setStrokeWidth(strokeWidthText);
}


第二步|绘图

//初始化半径,由于圆环的厚度也要占据一定的宽度,因此需要减除厚度值,这样才能保证圆环
radius = Math.min(getWidth() / 2, getHeight() / 2) - strokeWidthRing;
// 1.画圆环
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mPaintRing);
// 2.画刻度
drawMark(canvas);
// 3.画圆心
canvas.drawCircle(getWidth() / 2, getHeight() / 2, 5, mPaintDegree);
// 4.移动坐标中心到画布中心
canvas.save();
canvas.translate(getWidth() / 2, getHeight() / 2);
canvas.save();
// 5.画时针
drawHourMark(hourDegree, canvas);
// 6.画分针
drawMinuteMark(minuteDegree, canvas);
// 7.画秒针
drawSecondMark(secondDegree, canvas);
// 8.画时间文字
mPaintText.setTextSize(30);
mPaintText.setTextAlign(Paint.Align.CENTER);
canvas.drawText(XDate.fmtDate(mCurrentDate,"HH:mm:ss"), 0, -20, mPaintText);


完整代码

/**
* 绘图基础-时钟<br>
* 博客:<a href="http://blog.csdn.net/qq243223991">安前松博客</a>
*/
public class ClockView extends View {

private Paint mPaintRing;// 圆环画笔 private Paint mPaintDegree;// 刻度/圆心画笔 private Paint mPaintText;// 文字画笔 private float strokeWidthText = 2;// 文字画笔厚度 private float strokeWidthRing = 4;// 圆环画笔厚度 private float radius = 0;// 圆环半径 //用于初始化时间的角度设置 private float hourDegree = 0;// 时针角度 private float minuteDegree = 0;// 分针角度 private float secondDegree = 0;// 秒针角度 private Date mCurrentDate=null;// 用户设置时间,默认为当前时间

private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what==0){
long times=mCurrentDate.getTime();
mCurrentDate.setTime(times+1000);
setDate(mCurrentDate);
handler.sendEmptyMessageDelayed(0,1000);
}
}
};

public ClockView(Context context) {
super(context);
initPaint();
}

public ClockView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int min = Math.min(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));// 防止圆形变形,宽高度必须相等
setMeasuredDimension(min, min);// 重新设置宽高
}

/**
* 根据默认宽度测量宽度
*
* @param measureSpec
* @return
*/
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = 200;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}

/**
* 根据默认高度测量高度
*
* @param measureSpec
* @return
*/
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = 200;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}

private void initPaint() {
mCurrentDate=new Date();
setDate(mCurrentDate);
// 初始化圆环画笔
mPaintRing = new Paint();
mPaintRing.setColor(Color.RED);
mPaintRing.setStyle(Paint.Style.STROKE);
mPaintRing.setStrokeWidth(strokeWidthRing);
mPaintRing.setAntiAlias(true);
// 初始化刻度画笔
mPaintDegree = new Paint();
mPaintDegree.setColor(Color.RED);
mPaintDegree.setStyle(Paint.Style.FILL);
mPaintDegree.setAntiAlias(true);
// 初始化文字画笔
mPaintText = new Paint();
mPaintText.setColor(Color.RED);
mPaintText.setStyle(Paint.Style.FILL);
mPaintText.setAntiAlias(true);
mPaintText.setStrokeWidth(strokeWidthText);
}

@Override
protected void onDraw(Canvas canvas) {
//初始化半径,由于圆环的厚度也要占据一定的宽度,因此需要减除厚度值,这样才能保证圆环
radius = Math.min(getWidth() / 2, getHeight() / 2) - strokeWidthRing;
// 1.画圆环
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mPaintRing);
// 2.画刻度
drawMark(canvas);
// 3.画圆心
canvas.drawCircle(getWidth() / 2, getHeight() / 2, 5, mPaintDegree);
// 4.移动坐标中心到画布中心
canvas.save();
canvas.translate(getWidth() / 2, getHeight() / 2);
canvas.save();
// 5.画时针
drawHourMark(hourDegree, canvas);
// 6.画分针
drawMinuteMark(minuteDegree, canvas);
// 7.画秒针
drawSecondMark(secondDegree, canvas);
// 8.画时间文字
mPaintText.setTextSize(30);
mPaintText.setTextAlign(Paint.Align.CENTER);
canvas.drawText(XDate.fmtDate(mCurrentDate,"HH:mm:ss"), 0, -20, mPaintText);

}

/**
* 画时针
*
* @param degree
* @param canvas
*/
private void drawHourMark(float degree, Canvas canvas) {
canvas.rotate(degree);// 旋转度数
mPaintDegree.setStrokeWidth(6);
canvas.drawLine(0, 0, 0, -radius / 2, mPaintDegree);
canvas.restore();
canvas.save();
}

/**
* 画分针
*
* @param degree
* @param canvas
*/
private void drawMinuteMark(float degree, Canvas canvas) {
canvas.rotate(degree);// 旋转度数
mPaintDegree.setStrokeWidth(3);
canvas.drawLine(0, 0, 0, -radius / 2 - 25, mPaintDegree);
canvas.restore();
canvas.save();
}

/**
* 画秒针
*
* @param degree
* @param canvas
*/
private void drawSecondMark(float degree, Canvas canvas) {
canvas.rotate(degree);// 旋转度数
mPaintDegree.setStrokeWidth(2);
canvas.drawLine(0, 0, 0, -radius / 2 - 40, mPaintDegree);
canvas.restore();
canvas.save();
}

/**
* 画刻度
*/
private void drawMark(Canvas canvas) {
for (int i = 0; i < 60; i++) {
if (i % 5 == 0) {
mPaintDegree.setStrokeWidth(strokeWidthRing);
canvas.drawLine(getWidth() / 2, strokeWidthRing, getWidth() / 2, 40, mPaintDegree);
mPaintText.setTextAlign(Paint.Align.CENTER);
mPaintText.setTextSize(20);
if (i / 5 == 0) {
canvas.drawText("12", getWidth() / 2, 60, mPaintText);
} else {
canvas.drawText(i / 5 + "", getWidth() / 2, 60, mPaintText);
}
} else {
mPaintDegree.setStrokeWidth(2);
canvas.drawLine(getWidth() / 2, strokeWidthRing, getWidth() / 2, 30, mPaintDegree);
}
canvas.rotate(6, getWidth() / 2, getHeight() / 2);
}
}

/**
* 设置时间
*
* @param date
*/
public void setDate(@NonNull Date date) {
mCurrentDate=date;
float hourDegree = getHourDegree(getHours(date), getMinutes(date), getSeconds(date));
float minuteDegree = getMinuteDegree(getMinutes(date), getSeconds(date));
float secondDegree = getSecondDegree(getSeconds(date));
rotate(hourDegree, minuteDegree, secondDegree);
}

/**
* 设置时间戳
*
* @param timeMills
*/
public void setTimeMills(long timeMills) {
Date date = new Date();
date.setTime(timeMills);
setDate(date);
}

/**
* 开始时间旋转
*/
public void start(){
handler.sendEmptyMessageDelayed(0,1000);
}

/**
* 旋转
*/
private void rotate(float hourDegree, float minuteDegree, float secondDegree) {
this.hourDegree = hourDegree;
this.minuteDegree = minuteDegree;
this.secondDegree = secondDegree;
invalidate();
}

/**
* 根据小时获取角度
*
* @param hour
* @return
*/
private float getHourDegree(int hour, int minute, int second) {
float hourDegree = (hour + minute / 60.0f + second / 3600.0f) * 30;
return hourDegree;
}

/**
* 根据分钟获取角度
*
* @param minute
* @return
*/
private float getMinuteDegree(int minute, int second) {
float minuteDegree = (minute + second / 60.0f) * 6;
return minuteDegree;
}

/**
* 根据秒钟获取角度
*
* @param second
* @return
*/
private float getSecondDegree(int second) {
int secondDegree = second * 6;
return secondDegree;
}

/**
* 获取时间信息
*
* @param date
* @return
*/
private HashMap<Integer, Integer> getTime(Date date) {
HashMap<Integer, Integer> time = new HashMap<>();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
time.put(Calendar.HOUR, calendar.get(Calendar.HOUR));//12小时制
time.put(Calendar.MINUTE, calendar.get(Calendar.MINUTE));
time.put(Calendar.SECOND, calendar.get(Calendar.SECOND));
return time;
}

/**
* 获取当天中小时
*
* @param date
* @return
*/
private int getHours(Date date) {
return getTime(date).get(Calendar.HOUR);
}

/**
* 获取分钟
*
* @param date
* @return
*/
private int getMinutes(Date date) {
return getTime(date).get(Calendar.MINUTE);
}

/**
* 获取秒钟
*
* @param date
* @return
*/
private int getSeconds(Date date) {
return getTime(date).get(Calendar.SECOND);
}

}


觉得有用就支持一下,谢谢!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐