参照一个大神的Canvas介绍:http://blog.csdn.net/qinjuning/article/details/6936783
今天我们来学习一下如何用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)自定义view
public 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:模拟钟表
.自定义view
package 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);
}
});
}
}