本来前天应该整理的了,但是临时有任务给耽搁了,不过今天回过头来发现好多注意点都忘了,还是记录一下吧。
canvas绘制点,绘制线,绘制各种图形,其实这个可以说是最简单的了,也没有什么具体可以讲的,方法的参数含义基本上都可以理解,这里只说一点:圆弧或者扇形是的绘制如何确定。圆弧的绘制是这样的,他是将一个矩形(之所以不是正方形是因为也可以是椭圆的一部分)的内切圆的一部分截取出来的。那问题就是矩形如何确定呢?这里举个例子:例如所需扇形的半径为100,圆心坐标为(200,200),那么所需矩形的左上角坐标就为(100,100),右下角坐标为(300,300),我想规律已经出来了,没错就是这么算的,矩形的左上角坐标x=centerX-r,y=centerY-r ,矩形右下角坐标为 x=centerX+r y=centerY+r 。这个自己敲一下代码就看出来了。。。。其他方法,没什么好说的,自己敲一下代码就理解了,不多说了也,我把我的代码复制下来,里面也有注意的地方。
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.YELLOW);
canvas.drawPoint(50,50,mPaint);
//实心圆 圆心坐标x y 半径 r 画笔
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(100, 100, 80, mPaint);
//绘制圆弧(实心) 这里注意如何确定参数中第一个矩形的坐标如何确定,(centerX-r,centerY-r,centerX+r,centerY+r) 开始的角度 -90度为我们理解的0度方向 转过的角度 是否连接圆心
RectF oval = new RectF(0, 200, 200, 400);
// canvas.drawArc(oval,-90,90,false,mPaint);//圆弧
canvas.drawArc(oval, -90, 90, true, mPaint);//扇形
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(3);
RectF ov = new RectF(0, 350, 200, 550);
// canvas.drawArc(ov,-90,90,true,mPaint);//扇形
canvas.drawArc(ov, -90, 90, false, mPaint);//弧线
//椭圆
canvas.drawOval(0, 650, 200, 800, mPaint);
//矩形
Rect rect = new Rect(0, 850, 200, 1050);
canvas.drawRect(rect, mPaint);
//圆角矩形
canvas.drawRoundRect(250, 10, 450, 210, 15, 15, mPaint);
// 按照path写文字
mPaint.setTextSize(DensityUtils.sp2px(getContext(), 16));
mPaint.setStrokeWidth(1);
mPaint.setStyle(Paint.Style.FILL);
String te = "abcdefsgeg";
Path pa = new Path();
pa.addArc(300, 500, 900, 1000, -180, 120);
canvas.drawTextOnPath(te, pa, 0, 0, mPaint);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(3);
Path p = new Path();
p.moveTo(500, 800);
p.quadTo(650, 850, 450, 1150);//赛贝尔曲线
// p.cubicTo(100,200,200,500,6200,850);//两个控制点的赛贝尔曲线 http://www.cnblogs.com/lenve/p/5865874.html
canvas.drawPath(p, mPaint);
补充:看来确实是过了两天忘了很多东西,有两个重要的知识忘了记录了
1.绘制线,必须要说Paint中与线有关的参数,因为很重要,很重要
//绘制线的一些设置
mPaint.setStrokeCap(Paint.Cap.SQUARE);//Paint.Cap.BUTT 无延伸效果,是多大就多大 Paint.Cap.ROUND 延伸为圆角 Paint.Cap.SQUARE 延伸为矩形
mPaint.setStrokeJoin(Paint.Join.ROUND);//设置连接处效果 Paint.Join.BEVEL 有倒角,为直线倒角 Paint.Join.MITER 没有倒角 Paint.Join.ROUND 圆形倒角 详见:http://blog.csdn.net/abcdef314159/article/details/51720686
mPaint.setStrokeMiter(Paint.ANTI_ALIAS_FLAG);//设置画笔的倾斜度(不知有没有用)
mPaint.setHinting(Paint.HINTING_OFF);//设置画笔的隐藏模式 Paint.HINTING_OFF 关闭 Paint.HINTING_ON 打开
//TODO:特殊线需要单独研究
mPaint.setPathEffect(new CornerPathEffect(2));//有时候我们需要点画线等特殊的线,这个可以设置连接线的形状 CornerPathEffect 两段线之间用圆角 DashPathEffect将线段虚线话 DiscretePathEffect打散path效果 其他效果详见 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0120/2334.html
//mPaint.setRasterizer();//设置光栅,被废弃(无需研究)
下面举例说明两个参数的含义,其他可以看看里面引用的参考文章,讲的很好
mPaint.setStrokeCap(Paint.Cap.BUTT);//设置线的两端情况 默认为此状态即是多长就多长
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(25);
Path path=new Path();
path.moveTo(400,500);
path.lineTo(600,500);
canvas.drawPath(path,mPaint);
mPaint.setStrokeCap(Paint.Cap.ROUND);//两端为圆角伸出
Path path1=new Path();
path1.moveTo(400,550);
path1.lineTo(600,550);
canvas.drawPath(path1,mPaint);
mPaint.setStrokeCap(Paint.Cap.SQUARE);//两端为矩形伸出
Path path2=new Path();
path2.moveTo(400,600);
path2.lineTo(600,600);
canvas.drawPath(path2,mPaint);
mPaint.setStrokeCap(Paint.Cap.BUTT);
mPaint.setStrokeJoin(Paint.Join.BEVEL);//设置线与线的连接方式,此参数表示直切,还有圆角,直角,默认为直角
Path path3=new Path();
path3.moveTo(400,650);
path3.lineTo(600,650);
path3.lineTo(600,750);
canvas.drawPath(path3,mPaint);
效果就是最后第二张图片中的几条大粗线。
2.绘制点线面,不得不说一个path函数,非常非常重要 参见:http://www.cnblogs.com/lenve/p/5865874.html
下面开始绘制一个大家都会练习的一个例子:时钟。里面有很重要的内容,最好运行一下,仔细看思路。
public class TimeView extends TextView {
private Paint mPaint;
private int mSecond;
private int mMinite;
private int mHour;
public TimeView(Context context) {
this(context, null);
}
public TimeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TimeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
String time = getTimeShort();
int hour = Integer.valueOf(time.substring(0, 2));
mHour = hour % 12;
mMinite = Integer.valueOf(time.substring(3, 5));
mSecond = Integer.valueOf(time.substring(6, time.length()));
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
mSecond++;
if (mSecond == 60) {
mSecond = 0;
mMinite++;
if (mMinite == 60) {
mMinite = 0;
mHour++;
if (mHour == 12) {
mHour = 0;
}
}
}
invalidate();
break;
}
}
};
@Override
protected void onDraw(Canvas canvas) {
int screenX = getMeasuredWidth() / 2;
int screenY = getMeasuredHeight() / 2;
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(3);
mPaint.setColor(Color.GREEN);
//绘制背景
canvas.drawColor(Color.YELLOW);
//绘制大圆
canvas.drawCircle(screenX, screenY, 250, mPaint);
//绘制表芯小圆
mPaint.setColor(Color.GRAY);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(screenX, screenY, 18, mPaint);
//绘制指针小圆
mPaint.setColor(Color.GREEN);
canvas.drawCircle(screenX, screenY, 10, mPaint);
//绘制文字北京时间 问题:如何计算指定长度的字符所占的角度(未解决)
mPaint.setStrokeWidth(2);
String s = "北京时间";
mPaint.setTextSize(DensityUtils.sp2px(getContext(), 19));
Path textPath = new Path();
RectF rect = new RectF(screenX - 205, screenY - 205, screenX + 205, screenY + 205);
textPath.arcTo(rect, -111, 90, false);//这里没有做适配,所以可能运行不是在文字正上方
canvas.drawTextOnPath(s, textPath, 0, 0, mPaint);
//绘制刻度 这个不是很好理解:思路是这样的,首先将画布平移到圆的中心(本例子中就是屏幕中央),那么此时高中立体几何的知识就用到了,画布已经平移到屏幕中央了,那么
// 此时屏幕中央的坐标是多少呢?答案是(0,0),屏幕左上角的坐标是多少呢(-screenWidth/2,-screenHeight/2),为什么?想想立体几何中的知识(具体什么知识我也忘了,不过确实是)
//然后我们绘制刻度线就要以屏幕中央为基准点,绘制。怎么绘制?很简单,画笔只负责在同一个位置画直线,然后让画布旋转,相对运动吗,这样转一圈,刻度就好了
//现在是怎么确定直线两点坐标为题,我的思路是在正上方绘制直线,那么坐标就是多少呢?例如线长为20,圆半径为100,则坐标为(0,-100)和(0,-120).理解一下,再读读上面内容画个坐标系就懂了
canvas.save();//保存当前画布的各种属性状态,配合canvas.restore();使用
canvas.translate(screenX, screenY);
for (int i = 0; i < 60; i++) {
if (i % 5 == 0) {//长
mPaint.setStrokeWidth(3);
canvas.drawLine(0, -250, 0, -274, mPaint);
} else {//短
mPaint.setStrokeWidth(2);
canvas.drawLine(0, -250, 0, -265, mPaint);
}
canvas.rotate(6);
}
//绘制数字(思路和绘制刻度一样)
Rect re = new Rect();
for (int i = 1; i < 13; i++) {
int t = i - 1;
if (t == 0) {
t = 12;
}
mPaint.setStrokeWidth(2);
mPaint.setTextSize(DensityUtils.sp2px(getContext(), 16));
String num = String.valueOf(t);
mPaint.getTextBounds(num, 0, num.length(), re);
canvas.drawText(num, -re.width() / 2, -280, mPaint);
canvas.rotate(30);
}
canvas.restore();//回复保存的画布各种属性状态,这中间做的各种改变都放弃掉
canvas.save();
//绘制时针
mPaint.setStrokeWidth(7);
canvas.translate(screenX, screenY);
canvas.rotate((float) 30 * mHour + 30 * (float) mMinite / 60);
canvas.drawLine(0, 20, 0, -155, mPaint);
canvas.restore();
//绘制分针
canvas.save();
mPaint.setStrokeWidth(5);
canvas.translate(screenX, screenY);
canvas.rotate((float) 6 * mMinite + 6 * (float) mSecond / 60);
canvas.drawLine(0, 25, 0, -175, mPaint);
canvas.restore();
canvas.save();
//绘制秒针
canvas.translate(screenX, screenY);
mPaint.setStrokeWidth(3);
canvas.rotate(6 * mSecond);
canvas.drawLine(0, 35, 0, -190, mPaint);
canvas.restore();
mHandler.sendEmptyMessageDelayed(0, 1000);
}
/**
* 获取时间 小时:分;秒 HH:mm:ss
*
* @return
*/
public static String getTimeShort() {
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
Date currentTime = new Date();
return formatter.format(currentTime);
}
public void onDestory() {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}