canvas教程

Android 绘图基础:Canvas画布——自定义View基础(绘制表盘、矩形、圆形、

字号+ 作者:H5之家 来源:H5之家 2017-05-26 10:08 我要评论( )

今天学习了Canvas画布,感觉挺好玩的,通过它我们可以自定义一个View,设置View的相关效果之类的。感觉用法差不多,重要的是要理解方法中传入的参数的含义,比




  今天学习了Canvas画布,感觉挺好玩的,通过它我们可以自定义一个View,设置View的相关效果之类的。感觉用法差不多,重要的是要理解方法中传入的参数的含义,比如float类型的参数,传递的是坐标,已开是没有注意传入的参数时坐标,导致我迷糊了一段时间,希望大家不要犯我的错误,记住是坐标啊!。

一、Canvas画布介绍The Canvas class holds the “draw” calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).  

  关于Canvas网上也没有什么好的介绍, 我就直接从API中复制过来的,这里翻译一下。

  Canvas 支持“绘制”调用,想要进行绘制,你需要4个基本组成:一个由像素构成的位图,支持绘制调用的画布(绘制位图),一个图元(如矩形,路径,文本,位图),和油漆(用于绘制图的颜色和样式)。

(上面的四个基本组成会在后面代码部分进行解释)

  API中说的究竟是什么意思呢?我们可以把这个Canvas理解成系统提供给我们的一块内存区域(但实际上它只是一套画图的API,真正的内存是下面的Bitmap),而且它还提供了一整套对这个内存区域进行操作的方法,所有的这些操作都是画图API。这样我们就可以在画布上进行绘制了。(参考:Android利用Canvas绘制图形)

二、使用步骤  上面说了那么多是不是特别想知道它要怎样使用呢?现在我们就来说说他的使用步骤。

2.1使用步骤  1、写一个class类继承自View

2、在继承的class中写它的两个构造器,以前的版本中我们只需要写它的两个构造器(一个构造器参数为View(Context)另一个构造器参数为 View(Context AttributeSet) ),现在版本的升级又新出了两个构造器(View(Context attribute style)带有主题样式的),不管那么多,我们还是只写本来的两个构造器就OK了。

3、在该class中调用onDraw方法和onMeasure()方法。

4、在布局中使用该View

5、进行绘制

2.1使用示例   下面我们按照上面的步骤一步步进行

1、写一个class类继承自View

[code]public class MyView extends View { }

2、在继承的class中写它的两个构造器

[code]public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); }

3、在该class中重写onDraw方法和onMeasure()方法。

onDraw(我们将在该方法中进行绘制图形)

[code]@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas);

onMeasure(获得画布的高度与宽度)

[code]@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); }

补充:由于onMeasure方法中本身就有获取高度与宽度的方法,我们直接点进去(ctrl+左键),复制出来就可以了。

4、在布局中使用该View(布局时必须写全包名+类名)

[code]<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <com.example.myview.MyView android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>

5、进行绘制

我们要如何进行绘制呢?首先我们看些Canvas都能绘制写什么。

图元



drawRect(float left, float top, float right, float bottom, Paint paint)

//绘制矩形(左上右下的坐标,我们可以理解为左上的坐标为我们确定了矩形的左上角点,右下坐标为我们确定了右下角的点,这样一个矩形区域就确定出来了,paint为画笔)

drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)

//绘制圆角矩形(左上右下的坐标)

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

//绘制弧(参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,参数二是起始角(度)在电弧的开始,

参数三扫描角(度)开始顺时针测量的,参数四是如果这是真的话,包括椭圆中心的电弧,并关闭它,实际绘制的是扇形,如果它是假这将是一个弧线,参数五是Paint对象)

drawOval(float left, float top, float right, float bottom, Paint paint)

//绘制椭圆

drawLine(float startX, float startY, float stopX, float stopY, Paint paint)

//绘制直线(XY起止坐标)

drawCircle(float cx, float cy, float radius, Paint paint)

//绘制圆形

drawText(String text, float x, float y, Paint paint)

//绘制文本

其他的不再进行列举,有需要可以查看API文档。



在图元的参数中可以看到我们画图是需要画笔的,下面我们再来看下画笔(画漆)。这里展示一段代码相信你就能够明白了。

[code]Paint mpaintline = new Paint(); mpaintline.setColor(Color.RED); //设置线宽 mpaintline.setStrokeWidth(10); //设置抗锯齿 mpaintline.setAntiAlias(true);

[code]//设置空心,比如画实心圆还是空心圆 mpaintcircle.setStyle(Paint.Style.STROKE);

[code] mpainttext=new Paint(); mpainttext.setTextSize(30); //设置字的位置 mpainttext.setTextAlign(Align.CENTER); mpainttext.setColor(Color.BLACK);

2.2表盘示例上面说了那么多,我们来看一个具体而完整的实例,来整理一下思路。



[code]public class MyView extends View { private int width; private int height; private Paint mpaintline; private Paint mpaintcircle; private Paint mpainttext; private Calendar mcalendar; private static final int NEED_INVALIDATE=0x23; private Handler mhandler=new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case NEED_INVALIDATE: //重新获取时间 mcalendar=Calendar.getInstance(); //重新绘制界面 invalidate();//告诉UI主线程重新绘制 //再次发送消息,递归调用,再次监测秒针 mhandler.sendEmptyMessageDelayed(NEED_INVALIDATE, 1000); break; default: break; } }; }; public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { //为了不是每次都创建Paint,我们在这里创建 super(context, attrs); mcalendar=Calendar.getInstance(); mpaintline = new Paint(); mpaintline.setColor(Color.RED); //设置线宽 mpaintline.setStrokeWidth(10); //设置抗锯齿 mpaintline.setAntiAlias(true); mpaintcircle = new Paint(); mpaintcircle.setColor(Color.GREEN); mpaintcircle.setStrokeWidth(10); //设置空心 mpaintcircle.setStyle(Paint.Style.STROKE); mpainttext=new Paint(); mpainttext.setTextSize(30); mpainttext.setTextAlign(Align.CENTER); mpainttext.setColor(Color.BLACK); //发送消息,监测秒针 mhandler.sendEmptyMessage(NEED_INVALIDATE); } //在此方法中进行绘制 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制圆形 canvas.drawCircle(width/2, height/2, width/2-20, mpaintcircle); //绘制表芯 canvas.drawCircle(width/2, height/2, 5, mpaintcircle); //绘制表盘时我们采用旋转画布的思想,让画布进行旋转一定角度,绘制 for(int i=1;i<=12;i++){ //保存画布当前状态 canvas.save(); //指定旋转角度与旋转点 canvas.rotate(360/12*i,width/2,height/2); //绘制表盘 canvas.drawLine(width/2, height/2-(width/2-20), width/2,height/2-width/2+40, mpaintline); //绘制文字 canvas.drawText(""+i, width/2, height/2-width/2+70, mpainttext); //恢复开始位置 canvas.restore(); } int minute=mcalendar.get(Calendar.MINUTE); int hour=mcalendar.get(Calendar.HOUR); int second=mcalendar.get(Calendar.SECOND); float minudegrees=minute/60f*360; float hourdregrees=hour*60/12f/60*360; float senconddegree=second/60f*360; //绘制分针 canvas.save(); canvas.rotate(minudegrees,width/2,height/2); canvas.drawLine(width/2, height/2, width/2, height/2-width/2+90, mpaintline); canvas.restore(); //绘制时针 canvas.save(); canvas.rotate(hourdregrees,width/2,height/2); canvas.drawLine(width/2, height/2, width/2, height/2-width/2+100, mpaintline); canvas.restore(); //绘制秒针 canvas.save(); canvas.rotate(senconddegree,width/2,height/2); canvas.drawLine(width/2, height/2, width/2, height/2-width/2+60, mpaintline); canvas.restore(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //固定 width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); } }

2.3绘制圆形progressbar




这里模拟下载,自定义了一个ProgressBar。在MyProgressCircle 中定义出最大进度和当前进度,给出当前进度的SET\GET方法,通过Mainactivity中改变Progres的当前进度,根据当前进度,不停修改半径,重新绘制表层绿色圆形。

[code]public class MyProgressCircle extends View{ private int width; private int height; private int Maxprogress=100; private int Currentprogress; private Paint PaintBackGround; private Paint PaintCurrent; private Paint PaintText; public int getMaxprogress() { return Maxprogress; } public void setMaxprogress(int maxprogress) { Maxprogress = maxprogress; } //当前进度的set、get方法 public int getCurrentprogress() { return Currentprogress; } public void setCurrentprogress(int currentprogress) { Currentprogress = currentprogress; invalidate(); } public MyProgressCircle(Context context) { super(context); // TODO Auto-generated constructor stub } public MyProgressCircle(Context context, AttributeSet attrs) { super(context, attrs); PaintBackGround=new Paint(); PaintBackGround.setColor(Color.GRAY); PaintBackGround.setAntiAlias(true); PaintBackGround.setStrokeWidth(10); PaintCurrent=new Paint(); PaintCurrent.setColor(Color.GREEN); PaintCurrent.setAntiAlias(true); PaintCurrent.setStrokeWidth(10); PaintText=new Paint(); PaintText.setColor(Color.BLACK); PaintText.setAntiAlias(true); PaintText.setTextAlign(Align.CENTER); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(width/2, height/2,width/2-30 , PaintBackGround); //根据当前进度改变半径,绘制圆形 canvas.drawCircle(width/2, height/2,(float) (width/2-30)*Currentprogress/Maxprogress, PaintCurrent); canvas.drawText(Currentprogress*100f/Maxprogress+"%", width/2,height/2, PaintText); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); } }

MainActivity中改变progress进度

[code]public class MainActivity extends Activity implements OnClickListener{ private Button btn_circle_progress; private MyProgressCircle myprogresscircle; private int progress; private Handler mhanHandler=new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case 0x23: progress++; if(progress<=100){ myprogresscircle.setCurrentprogress(progress); mhanHandler.sendEmptyMessageDelayed(0x23, 200); } break; default: break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.circle_progress); btn_circle_progress=(Button) findViewById(R.id.mbtn_circleprogress); myprogresscircle=(MyProgressCircle) findViewById(R.id.mycircleprogress); btn_circle_progress.setOnClickListener(this); } @Override public void onClick(View v) { mhanHandler.sendEmptyMessageDelayed(0x23, 1000); } }

2.4绘制弧形progressbar



[code] @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //设置画弧的区域 RectF oval1=new RectF(width/2-100,height/2-100,width/2+100,height/2+100); canvas.drawArc(oval1, 270, Currentprogress, false, PaintCurrent);//第二个参数:顺时针起始位置;第三个参数:顺时针转的弧 canvas.drawText(Currentprogress*100f/Maxprogress+"%", width/2,height/2, PaintText); }

MainActivity

[code]public class MainActivity_ARC extends Activity implements OnClickListener{ private Button btn_arc_progress; private MyArcProgress myprogressarc; private int progress; private Handler mhanHandler=new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case 0x23: progress++; if(progress<=360){ myprogressarc.setCurrentprogress(progress); mhanHandler.sendEmptyMessageDelayed(0x23, 200); } break; default: break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.arc_progress); btn_arc_progress=(Button) findViewById(R.id.mbtn_arcprogress); myprogressarc= (MyArcProgress) findViewById(R.id.myarcprogress); btn_arc_progress.setOnClickListener(this); } @Override public void onClick(View v) { mhanHandler.sendEmptyMessageDelayed(0x23, 1000); } }

2.5绘制多条线与矩形



[code]public class MyRectProgress extends View{ private int width; private int height; private int Maxprogress=100; private int Currentprogress; private Paint PaintBackGround; private Paint PaintCurrent; private Paint PaintText; private int i; private float[] lines=new float[400]; public int getMaxprogress() { return Maxprogress; } public void setMaxprogress(int maxprogress) { Maxprogress = maxprogress; } public int getCurrentprogress() { return Currentprogress; } public void setCurrentprogress(int currentprogress) { Currentprogress = currentprogress; lines[i]=50; i++; lines[i]=50+250f*Currentprogress/Maxprogress; i++; lines[i]=300; i++; lines[i]=50+250f*Currentprogress/Maxprogress; invalidate(); } public MyRectProgress(Context context) { super(context); // TODO Auto-generated constructor stub } public MyRectProgress(Context context, AttributeSet attrs) { super(context, attrs); PaintBackGround=new Paint(); PaintBackGround.setColor(Color.GRAY); PaintBackGround.setAntiAlias(true); PaintBackGround.setStrokeWidth(10); PaintCurrent=new Paint(); PaintCurrent.setColor(Color.GREEN); PaintCurrent.setAntiAlias(true); PaintCurrent.setStrokeWidth(5); PaintText=new Paint(); PaintText.setColor(Color.BLACK); PaintText.setAntiAlias(true); PaintText.setTextAlign(Align.CENTER); PaintText.setTextSize(30); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //canvas.drawCircle(width/2, height/2,width/2-30 , PaintBackGround); canvas.drawRect(50,50 ,300, 300, PaintBackGround); canvas.drawLines(lines, PaintCurrent); //canvas.drawLine(50,50+250f*Currentprogress/Maxprogress, 300, 50+250f*Currentprogress/Maxprogress, PaintCurrent); canvas.drawText(Currentprogress*100f/Maxprogress+"%", width/2,height/2, PaintText); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); } }

2.5绘制渐变样式就是我们开始时图片展示的样式

[code]package com.example.myview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.RectF; import android.graphics.Shader; import android.util.AttributeSet; import android.view.View; public class MyotherProgress extends View{ private int width; private int height; private int Maxprogress=100; private int Currentprogress; private Paint PaintBackGround; private Paint PaintCurrent; private Paint PaintText; public int getMaxprogress() { return Maxprogress; } public void setMaxprogress(int maxprogress) { Maxprogress = maxprogress; } public int getCurrentprogress() { return Currentprogress; } public void setCurrentprogress(int currentprogress) { Currentprogress = currentprogress; invalidate(); } public MyotherProgress(Context context) { super(context); // TODO Auto-generated constructor stub } public MyotherProgress(Context context, AttributeSet attrs) { super(context, attrs); PaintBackGround=new Paint(); PaintBackGround.setColor(Color.GRAY); PaintBackGround.setAntiAlias(true); PaintBackGround.setStrokeWidth(10); PaintCurrent=new Paint(); PaintCurrent.setColor(Color.GREEN); PaintCurrent.setAntiAlias(true); PaintCurrent.setStrokeWidth(10); /* 设置渐变色 颜色是改变的 */ Shader mShader = new LinearGradient(0, 0, 100, 100, new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.LTGRAY }, null, Shader.TileMode.REPEAT); PaintCurrent.setShader(mShader); PaintText=new Paint(); PaintText.setColor(Color.BLACK); PaintText.setAntiAlias(true); PaintText.setTextAlign(Align.CENTER); PaintText.setTextSize(30); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //底层圆形 canvas.drawCircle(width/2, height/2,width/2-30 , PaintBackGround); // 设置个新的长方形,扫描测量 RectF oval = new RectF(30,height/2-(width/2-30 ) , width-30 , height/2+width/2-30 ); //绘制扇形 // 画弧,第一个参数是RectF(确定绘制区域): //该类是第二个参数是角度的开始, //第三个参数是多少度, //第四个参数是真的时候画扇形,是假的时候画弧线 canvas.drawArc(oval, 0, Currentprogress*360f/100, true, PaintCurrent); canvas.drawText(Currentprogress*100f/Maxprogress+"%", width/2,height/2, PaintText); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); } }

想要了解更详细的可以再参考下面讲解





关于onMeasure方法的介绍

onMeasure

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • Android Canvas练习(3)自已绘柱形图(Bar Chart)

    Android Canvas练习(3)自已绘柱形图(Bar Chart)

    2017-05-26 11:00

  • 8.1.3 setStyle方法:设置风格

    8.1.3 setStyle方法:设置风格

    2017-05-26 09:05

  • 关于Android中Canvas画图的问题,求思路

    关于Android中Canvas画图的问题,求思路

    2017-05-26 08:01

  • 【Android】自己定义View、画家(画布)Canvas与画笔Paint的应用

    【Android】自己定义View、画家(画布)Canvas与画笔Paint的应用

    2017-05-26 08:00

网友点评