图形绘制之——Canvas详解(一)
2015-09-16 14:53
246 查看
参照一个大神的Canvas介绍:/article/1390946.html
今天我们来学习一下如何用Canvas来绘制图形。
这里我们做个比喻:
Paint 就是画笔
Bitmap 就是画布
Canvas 就是画家
这是网上一个大神的比喻,我觉得很形象,这里借用一下哈。
1)必须重写的两个构造器:
必须要重写带有AttributeSet 的那个构造器,因为关于xml中填写的信息都在AttributeSet 中,如果要是xml布局中可以使用,就必须调用这个构造器。
2)重写两个方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
}//获得在布局文件中对view控件的属性设置
和 protected void onDraw(Canvas canvas) {}//onDraw是UI主线程自动调用,只需要在此进行绘制即可。
2.在布局中使用自定义布局
使用全称。
例:
3.画笔的使用
1>画笔new对象时,不要在onDraw中进行,因为那样会使每次绘制都创建画笔,这是没有必要的。在有AttributeSet构造器中创建并设置属性,在这个构造器中设置,xml中才能设置上。
2>画笔的各个属性的设置:
图形画笔:
setColor()
setAntiAlias(true):设置抗锯齿
setStyle(Paint.Style.STROKE):设置空心
setAlpha(int a):设置透明度
设置文字画笔:
setTextAlign(Paint.Align.CENTER);
setTextSize()
4.canvas中的方法:(参数查看API)
drawLine( ):画线
drawRect( ):画矩形
drawText():画文字
drawCircle():画圆
drawArc():画弧
translate():平移
rotate():旋转
解释旋转:旋转与canvas.save()和canvas.restore()相互结合,旋转前保存,将画布逆时针旋转一个角度,画好后,利用canvas.restore()再恢复到原来位置,这在后面钟表的示例中有详细讲解。
刷新:
invalidate();//每次改变值后,需要重写绘制图形时,就调用此方法进行刷新。
2)在xml中引用:
注意:
1)角度:准确计算画布旋转的角度,注意转换成float类型。
2)canvas.save()和 canvas.restore()相互结合使用。
3)线程读秒:由于秒需要每秒一刷新,所以用线程去操作,但是由于不允许非UI线程去操作View界面,所以采用Handler。
4)由于分和秒不需要每秒都刷新,可以将他们建立到另一个view其实,避免每次重画,这里并没有这样做,有兴趣的朋友可以做一下试试哈。
5)这里在利用旋转画短线时,是每次利用for循环,每次刷新都进行旋转,划线的操作。
效果演示:
2)activity中,调用progress
今天我们来学习一下如何用Canvas来绘制图形。
这里我们做个比喻:
Paint 就是画笔
Bitmap 就是画布
Canvas 就是画家
这是网上一个大神的比喻,我觉得很形象,这里借用一下哈。
自定义ViewGrop/View:
这里我们就先不介绍自定义ViewGrop,当然自定义控件还可以继承SurfaceView,这里我也先不做介绍,就单单来看自定义的View吧。方法:
1.继承View类:1)必须重写的两个构造器:
public MyProgress(Context context) { super(context); } public MyProgress(Context context, AttributeSet attrs) {super(context, attrs); }
必须要重写带有AttributeSet 的那个构造器,因为关于xml中填写的信息都在AttributeSet 中,如果要是xml布局中可以使用,就必须调用这个构造器。
2)重写两个方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
}//获得在布局文件中对view控件的属性设置
和 protected void onDraw(Canvas canvas) {}//onDraw是UI主线程自动调用,只需要在此进行绘制即可。
2.在布局中使用自定义布局
使用全称。
例:
<com.example.day0916.widget.MyCupView> ……………… </com.example.day0916.widget.MyCupView>
3.画笔的使用
1>画笔new对象时,不要在onDraw中进行,因为那样会使每次绘制都创建画笔,这是没有必要的。在有AttributeSet构造器中创建并设置属性,在这个构造器中设置,xml中才能设置上。
2>画笔的各个属性的设置:
图形画笔:
setColor()
setAntiAlias(true):设置抗锯齿
setStyle(Paint.Style.STROKE):设置空心
setAlpha(int a):设置透明度
设置文字画笔:
setTextAlign(Paint.Align.CENTER);
setTextSize()
4.canvas中的方法:(参数查看API)
drawLine( ):画线
drawRect( ):画矩形
drawText():画文字
drawCircle():画圆
drawArc():画弧
translate():平移
rotate():旋转
解释旋转:旋转与canvas.save()和canvas.restore()相互结合,旋转前保存,将画布逆时针旋转一个角度,画好后,利用canvas.restore()再恢复到原来位置,这在后面钟表的示例中有详细讲解。
刷新:
invalidate();//每次改变值后,需要重写绘制图形时,就调用此方法进行刷新。
范例1:简单绘画
1)自定义viewpublic class MyView extends View { private int width; private int height; private Paint paintLine; private Paint paintCircle; public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); paintLine = new Paint(); paintLine.setColor(Color.BLUE); paintLine.setStrokeWidth(10); paintLine.setStyle(Paint.Style.STROKE);//设置空心 paintLine.setAntiAlias(true);//设置抗锯齿 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//获得组件view的宽和高 super.onMeasure(widthMeasureSpec, heightMeasureSpec); width=getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec); height=getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec); setMeasuredDimension(width,height); } @Override protected void onDraw(Canvas canvas) {//绘制 super.onDraw(canvas); canvas.drawLine(10,10,60,60,paintLine); } }
2)在xml中引用:
<com.example.day0916.widget.MyView android:layout_width="match_parent" android:layout_height="match_parent"> </com.example.day0916.widget.MyView>
范例2:模拟钟表
.自定义viewpackage com.example.day0916.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.os.Message; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import com.example.day0916.R; import java.util.Calendar; import java.util.logging.LogRecord; public class MyView extends View { private int width; private int height; private Paint paintLine; private Paint paintCircle; private Paint paintText; private int circle=200; private Calendar mCalender; private final int MESSAGE_WHAT=0x34; private Handler handler= new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case MESSAGE_WHAT: mCalender=Calendar.getInstance();//每次刷新前需要重新获得Calendar invalidate();//告诉主线程进行绘制 handler.sendEmptyMessageDelayed(MESSAGE_WHAT,1000);//每延迟1s让主线程绘制 break; default: break; } } }; public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); paintLine = new Paint(); paintLine.setColor(Color.BLUE); paintLine.setStrokeWidth(10); paintLine.setStyle(Paint.Style.STROKE);//设置空心 paintLine.setAntiAlias(true);//设置抗锯齿 //圆 paintCircle = new Paint(); paintCircle.setColor(Color.RED); paintCircle.setStrokeWidth(10); paintCircle.setStyle(Paint.Style.STROKE);//设置空心 paintCircle.setAntiAlias(true);//设置抗锯齿 //写字 paintText = new Paint(); paintText.setColor(Color.GREEN); paintText.setTextSize(30); paintText.setTextAlign(Paint.Align.CENTER);//设置字体居中 mCalender=Calendar.getInstance(); handler.sendEmptyMessage(MESSAGE_WHAT); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//获得组件view的宽和高 super.onMeasure(widthMeasureSpec, heightMeasureSpec); width=getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec); height=getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec); setMeasuredDimension(width,height); } @Override protected void onDraw(Canvas canvas) {//绘制 super.onDraw(canvas); // canvas.drawLine(10,10,60,60,paintLine);\ canvas.drawCircle(width / 2, height / 2, circle, paintCircle); canvas.drawCircle(width / 2, height / 2, 5, paintCircle); for(int i = 1;i<=12;i++){//注:旋转是先将画布逆时针旋转,划线,在复原 canvas.save();//保存当前画布状态 canvas.rotate(360/12*i,width/2,height/2);//旋转 canvas.drawLine(width / 2, height / 2 - circle, width / 2, height / 2 - circle + 20, paintLine);//小线的长度为20像素 canvas.drawText("" + i, width / 2, height / 2 - circle + 50, paintText);//参数:字符,写入的坐标x.y,画笔(由于画笔已经设置成居中) canvas.restore();//与save配合使用 } //得到当前的小时和分钟: int minute = mCalender.get(Calendar.MINUTE); int hour = mCalender.get(Calendar.HOUR); int second = mCalender.get(Calendar.SECOND); Log.d("time",minute+","+hour+""); //画分针: float degreeminute = 360/60f*minute; canvas.save(); canvas.rotate(degreeminute,width/2,height/2); canvas.drawLine(width / 2, height / 2 - circle + 100, width / 2, height / 2 + 2, paintLine); canvas.restore(); //画时针 float degreehour = (hour*60f+minute)/(12*60)*360;//时间f的位置 canvas.save(); canvas.rotate(degreehour,width/2,height/2); canvas.drawLine(width/2,height/2-circle+150,width/2,height/2+2,paintLine); canvas.restore(); //画秒针: float degreesecond = 360/60f*second; canvas.save(); canvas.rotate(degreesecond,width/2,height/2); canvas.drawLine(width/2,height/2-circle+70,width/2,height/2+2,paintLine); canvas.restore(); } }
注意:
1)角度:准确计算画布旋转的角度,注意转换成float类型。
2)canvas.save()和 canvas.restore()相互结合使用。
3)线程读秒:由于秒需要每秒一刷新,所以用线程去操作,但是由于不允许非UI线程去操作View界面,所以采用Handler。
4)由于分和秒不需要每秒都刷新,可以将他们建立到另一个view其实,避免每次重画,这里并没有这样做,有兴趣的朋友可以做一下试试哈。
5)这里在利用旋转画短线时,是每次利用for循环,每次刷新都进行旋转,划线的操作。
效果演示:
范例3:模拟下载(1)
1)自定义view:public class MyProgress extends View{ private int width; private int height; private int maxProgress=100; private int currentProgress=0; private Paint paintBackCircle; private Paint paintCurrentCircle; private Paint paintText; private int backCricleR=200; private int currenCircleR;//中间的小圆半径从0开始 public int getMaxProgress() { return maxProgress; } public void setMaxProgress(int maxProgress) { this.maxProgress = maxProgress; } public int getCurrentProgress() { return currentProgress; } public void setCurrentProgress(int currentProgress) { this.currentProgress = currentProgress; invalidate();//刷新界面************ } public MyProgress(Context context) { super(context); } public MyProgress(Context context, AttributeSet attrs) { super(context, attrs); //后面的圆的画笔 paintBackCircle = new Paint(); paintBackCircle.setColor(Color.GRAY); paintBackCircle.setAntiAlias(true); //前面的圆的画笔 paintCurrentCircle = new Paint(); paintCurrentCircle.setColor(Color.BLUE); paintCurrentCircle.setAntiAlias(true); //文字的画笔 paintText=new Paint(); paintText.setAntiAlias(true); paintText.setTextAlign(Paint.Align.CENTER); paintText.setColor(Color.BLACK); paintText.setTextSize(20); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width=getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec); height=getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec); setMeasuredDimension(width,height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(width / 2, height / 2, backCricleR, paintBackCircle); canvas.drawCircle(width/2,height/2,backCricleR*currentProgress/maxProgress,paintCurrentCircle); canvas.drawText(currentProgress+"%",width/2,height/2,paintText); } }
2)activity中,调用progress
package com.example.day0916; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import com.example.day0916.widget.MyProgress; public class MainActivity extends Activity { private MyProgress myProgress; private Button mButtonProgress; private final int MSG_WHAT_PROGRESS = 0x45; private int count = 0; Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case MSG_WHAT_PROGRESS: count++; if(count <= 100) { myProgress.setCurrentProgress(count); handler.sendEmptyMessageDelayed(MSG_WHAT_PROGRESS, 5000); } break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myProgress = (MyProgress) findViewById(R.id.progress); mButtonProgress= (Button) findViewById(R.id.buttonProgress); mButtonProgress.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { handler.sendEmptyMessage(MSG_WHAT_PROGRESS); } }); } }
相关文章推荐
- 自定义View之onMeasure()
- linux mutext spinlock 性能分析
- OSPF-7种类型LSA
- Hibernate – Many-to-Many example (XML Mapping)
- 对提高HBase写性能的一些思考
- 使用syslog记录Cisco设备日志
- 大型网站架构演变和知识体系
- 代码签名探析
- leetcode Pascal's Triangle
- SQLite三种JDBC驱动的区别
- MySql 启动报错:The server quit without updating PID file 如何解决
- [Elasticsearch] 邻近匹配 (二) - 多值字段,邻近程度与相关度
- Object-C 入门
- Lowest Common Ancestor of a Binary Search Tree (BST)
- python 类属性 实例属性 类方法 实例方法 静态方法(转载)
- Hibernate的三种状态
- CISCO ASA配置说明
- 调试笔记之 Flash Magic 下载程序 遇到的一些问题
- hp m425dn 打印机ADC进纸器复印或者扫描全黑
- BGP