Android Canvas、Paint、Path、drawBitmap

来源:互联网 发布:easybcd怎么引导linux 编辑:程序博客网 时间:2024/05/29 10:06

我们平常的画图都是通过Paint画笔在Canvas画布上进行绘制的,我们在画图之前首先就是要把我们的画笔设置好。就跟我们平时画图一样,先要选好画笔的粗细、颜色以及透明度,然后我们才开始作画,最后将Canvas画布呈现给用户。
这里写图片描述
我们来认识一些具体的方法:
画图之前我们是要准备好Pint(画笔)的,那么我们就来整一个画笔对象。
先了解一下画笔的一些样式
Paint.Style.FILL:填充内部也就是我们平时所说的实心的
Paint.Style.FILL_AND_STROKE :填充内部和描边 描边其实就是我们事先画好一个模型,然后再填充颜色空心圆到实心圆的变化
Paint.Style.STROKE :描边也就是我们所说的空心的
Paint paint= new Paint(Paint.ANTI_ALIAS_FLAG);
Paint.ANTI_ALIAS_FLAG就是消除锯齿使用以后边界就会变的稍微有点模糊,锯齿就看不到了
paint.setColor(Color.RED)设置画笔的颜色,画笔的颜色可以是系统的也可以自己设置
paint.setStrokeWidth(3);设置空心画笔的宽度,画笔的粗细可以通过此方法进行设置
有点也就有线,canvas里面有一个drawLine方法可以通过canvas.drawLine来画一条线。有线也有面的我们可以通过
canvas.drawLine(100,100,200,200,paint);里面的前四个参数分别是float startX, float startY, float stopX, float stopY。因为我们说话的其实是一条对角线,所以我们就以下面的矩形草图的AC两点来说明。startX则相当于A点的X坐标,startY则是A点的Y坐标,stopX则相当于C点的X坐标,stopY则是C点的Y坐标。
canvas.drawRect方法来画一个区域
这里写图片描述
示例代码为

@Override        protected void onDraw(Canvas canvas) {            canvas.drawLine(100,100,200,200,paint);            canvas.drawRect(100,300,200,400,paint);               }

我们还要了解一下paint的一些其他样式,以备用时之需。
paint.setStrokeJoin(Join join)设置结合处的样子使用为paint.setStrokeJoin(Paint.Join.ROUND),Round:结合处为圆弧,Miter:结合处为锐角, BEVEL:结合处为直线。设置时要注意:Paint.Style.FILL为默认的画笔样式,但是此种样式效果会显示不出来,所以建议使用另外Paint.Style.FILL_AND_STROKE 和Paint.Style.STROKE 两种样式其中的一种。
效果图为
这里写图片描述
示例代码为

@Override        protected void onDraw(Canvas canvas) {         paint.setStrokeWidth(20);            paint.setStyle(Paint.Style.STROKE);            paint.setStrokeJoin(Paint.Join.ROUND);            canvas.drawRect(160, 80, 250, 180, paint);            paint.setStrokeJoin(Paint.Join.MITER);            canvas.drawRect(160, 240, 250, 340, paint);            paint.setStrokeJoin(Paint.Join.BEVEL);            canvas.drawRect(160, 400, 250, 500, paint);        }

setStrokeCap(Cap cap)设置线末端的
paint.setStrokeCap(Paint.Cap.ROUND);它也有三种样式分别为ROUND、BUTT、SQUARE画笔样式虽然三种都可以设置,但是此种样式下显示的没有空心效果的,而且画笔在显示的区域内越粗效果越明显。由于是设置线的末端因此要调用的是canvas.drawLine的方法。
效果图为
这里写图片描述
示例代码为

@Override        protected void onDraw(Canvas canvas) {        paint.setStrokeWidth(100);            paint.setStyle(Paint.Style.STROKE);            paint.setStrokeCap(Paint.Cap.ROUND);            canvas.drawLine(160, 80, 250, 80, paint);            paint.setStrokeCap(Paint.Cap.BUTT);            canvas.drawLine(160, 240, 250, 240, paint);            paint.setStrokeCap(Paint.Cap.SQUARE);            canvas.drawLine(160, 400, 250, 400, paint);        }

Path类可以预先在View上将N个点连成一条”路径”,然后调用Canvas的drawPath(path,paint)即可沿着路径绘制图形
使用path也是要实例化一个对象然后调用其内部方法。
下面我们先来画一个三角形
这里写图片描述
示例代码为

@Override        protected void onDraw(Canvas canvas) {            Path path=new Path();            path.moveTo(210,210);            path.lineTo(210,350);            path.lineTo(280,350);            canvas.drawPath(path,paint);                 }

画图肯定是是起笔点的位置,path.moveTo就是先把一个起点移动到屏幕的某个位置,然后再以这一点开始移动到其他坐标点连城线,图形的形成就是先有点到线,再有先到面,点与点之间连成线,线与线之间连接起来就是一个面,上面的代码就是把起点移动到下图(下图只是一个草图,方便讲解而已)的A点path.moveTo(210,210);public void moveTo(float x, float y)通过对比很容易发现,参数里面的第一个210就是Ax ,第二个210就是Ay。path.lineTo(210,350)lineTo很明显就是连线到的意思,就是在起点和lineTo的坐标点之间连成一条线(AB),最后一个lineTo也是一样,不过是与前面lineTo的点连成一条线(BC),最后就是闭合,也就是最后一个lineTo点连接起点,形成一条线也就是我们看到的(AC),我们看到的图形就是这么形成的。
这里写图片描述
关于四边形的我们上面的canvas.drawRect(100,300,200,400,paint);
如果不知道如何设置float left, float top, float right, float bottom, 这几个参数,可以参考下面的关于正方形ABCD四个顶点的草图。left就是A和B的X坐标值,top就是A和D的Y坐标值,right就是C和D的X坐标值,bottom就是B和C的Y坐标值。记住这几点就会很容易的运用drawRect方法画出我们想要的矩形了。
已经实现过了,但是我们也可以通过path来完成
图形还是那个图形
这里写图片描述
示例代码为

@Override        protected void onDraw(Canvas canvas) {            Path path=new Path();            path.moveTo(100,300);            path.lineTo(100,400);            path.lineTo(200,400);            path.lineTo(200,300);            canvas.drawPath(path,paint);                 }

作图前要先想好画什么样的图形,如果是正方形,那么就要考虑到正方形的四个顶点的位置,如何确定呢,关于lineTo上面画三角形的时候已经介绍过,可以看一下。我们首先要确定四个顶点,起点的位置可以随意锚点,但是剩下的三个点就要按照一定的方式去设置了。如下图,加入我们以A点为起点,那么B点的X坐标就要跟A点的一样,那么Y轴呢?如果我们想画一个边长为100的正方形,我们就要在A点坐标的Y值上面加上100设置为B的Y点坐标值,这样B坐标就确定了,C点的坐标跟B点坐标的Y值是一样的,X坐标值是什么呢?我们可以看到C和B是在一条水平线上,C在B的右侧那么就要在B点的X坐标值上面加上100设置为C点坐标的X值,确定了B和C两点坐标的值就可以确定D点的了,因为D点坐标的X值其实就是A的Y坐标值,X值就是C的X坐标值。这样我们就可以完美的画出上面的矩形了。
这里写图片描述
我们如果想要扇形怎么办?我们可以通过canvas的drawArc方法来实现。drawArc方法里面含有四个参数分辨是:oval :指定圆弧的外轮廓矩形区域。startAngle: 圆弧起始角度,就是我们画笔的起点位置,单位为度。sweepAngle: 圆弧扫过的角度,也就是想要画的扇形的弧度,顺时针方向,单位为度。useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。我们先来实现一下
这里写图片描述
示例代码为

@Overrideprotected void onDraw(Canvas canvas) {     paint.setStrokeWidth(5);     paint.setStyle(Paint.Style.STROKE);     canvas.drawRect(50, 300, 150, 400, paint);     RectF rect = new RectF(50, 300, 150, 400);     canvas.drawArc(rect, -180, 180, true, paint);     paint.setStyle(Paint.Style.STROKE);     RectF rect1 = new RectF(200, 300, 300, 400);     canvas.drawArc(rect1, -180, 180, true, paint);     paint.setStyle(Paint.Style.STROKE);     RectF rect2 = new RectF(350, 300, 450, 400);     canvas.drawArc(rect2, -180, 180, false,paint); }

之所以第一个画出矩形是为了让大家知道,矩形就是扇形显示的区域,我们设置的是一个内切圆,矩形的中心就是圆心。通过上面的代码和效果图我们可以看到useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,来绘制扇形,如果为false的话就是一个弧线。
下面我们来实现一个大家都见过的一个自己画的钟表
这里写图片描述
示例代码

package demo.liuyongxiang.com.demo.activities;import android.app.Activity;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.RectF;import android.os.Bundle;import android.view.View;/** * Created by ytx on 2016/11/10. */public class MyCanvas extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(new CustomView1(this));    }    class CustomView1 extends View {        Paint paint;        public CustomView1(Context context) {            super(context);            paint = new Paint(); //设置一个笔刷大小是3的黄色的画笔            paint.setColor(Color.RED);        }        //在这里我们将测试canvas提供的绘制图形方法        @Override        protected void onDraw(Canvas canvas) {            paint.setStrokeWidth(5);            paint.setStyle(Paint.Style.STROKE);            paint.setAntiAlias(true);            paint.setStyle(Paint.Style.STROKE);            //将要画的位置移动到屏幕中间            canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);            //将位置移动画纸的坐标点:150,150            //以半径为150和180画圆            canvas.drawCircle(0, 0, 150, paint);            canvas.drawCircle(0, 0, 180, paint);            //使用path绘制路径文字            canvas.save();            //移动绘制文字的位置            canvas.translate(0, 0);            Path path = new Path();            //绘制的时候要注意左上不能大于右下,否则不会显示            RectF rect = new RectF(-100,-100,100,100);            path.addArc(rect, -220, 280);            Paint citePaint = new Paint(paint);            citePaint.setTextSize(28);            //设置画笔的粗细            citePaint.setStrokeWidth(3);            //float hOffset, float vOffset// 设置水平位置  vOffset  设置垂直位置            // 如果hOffset为0 说明开始位置在path.addArc设置的startAngle开始角度            // 如果vOffset 为0说明经过的位置是在与RectF的顶部相切处            canvas.drawTextOnPath("http://blog.csdn.net/u014452224", path, 14, 0, citePaint);            //为了方便一些转换操作,Canvas 还提供了保存和回滚属性的方法(save和restore),            // 比如你可以先保存目前画纸的位置(save),            // 然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置            canvas.restore();            Paint smallPaint = new Paint(paint); //非数字刻度画笔对象            smallPaint.setStrokeWidth(2);            smallPaint.setColor(Color.GRAY);            float  y=150;            int count = 60; //总刻度数            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);            paint.setStrokeWidth(6);            paint.setColor(Color.RED);            paint.setTextSize(24);            paint.setStrokeWidth(3);            for(int i=0 ; i <count ; i++){                if(i%5 == 0){                    //绘制数字刻度                    canvas.drawText(i == 0 ? "12" : String.valueOf(i / 5),((i / 5)>9||i==0)?-15f:-6f , -y-5f, paint);                }else{                    //绘制非数字的刻度                    canvas.drawLine(0f, y, 0f, y +15f, smallPaint);                }                canvas.rotate(360/count,0f,0f); //旋转画纸            }           //绘制秒针            paint.setColor(Color.RED);            paint.setStrokeWidth(4);            //画固定的圆圈            canvas.drawCircle(0, 0, 7, paint);            //画内部固定的点            paint.setStyle(Paint.Style.FILL);            //float cx, float cy, float radius, Paint paint            // cx中心点x坐标 cy中心点y坐标            canvas.drawCircle(0, 0, 5, paint);            //float startX, float startY, float stopX, float stopY, Paint paint            canvas.drawLine(0, 30, 0, -145, paint);        }    }}

对上面用到的canvas.save()和canvas.restore()来说明一下。它俩是两个相互匹配出现的,作用是用来保存画布的状态和取出保存的状态的。 当我们对画布进行旋转,缩放,平移等操作的时候其实我们是想对我们指定的元素进行操作,比如图片,一个矩形等,但是当你用canvas的方法来进行这些操作的时候,其实是对整个画布进行了操作,那么操作以后之前在画布上的元素都会受到影响,所以我们在对画布进行旋转,缩放,平移操作之前调用canvas.save()来保存画布当前的状态,当操作之后取出之前保存过的状态,这样就不会对其他的元素进行影响
我们来看看效果图就会知道怎么回事了
这里写图片描述
示例代码为

@Override        protected void onDraw(Canvas canvas) {            paint.setStrokeWidth(5);            paint.setStyle(Paint.Style.STROKE);            paint.setAntiAlias(true);            paint.setStyle(Paint.Style.STROKE);            canvas.translate(100, 100);            canvas.drawCircle(0, 0, 50, paint);            canvas.restore();            Path path=new Path();            path.moveTo(100,100);            path.lineTo(100,200);            path.lineTo(200,200);            path.lineTo(200,100);            path.close();            canvas.drawPath(path,paint);            canvas.translate(100, 300);            canvas.drawCircle(0, 0, 50, paint);            Path path1=new Path();            path1.moveTo(100,100);            path1.lineTo(100,200);            path1.lineTo(200,200);            path1.lineTo(200,100);            path1.close();            canvas.drawPath(path1,paint);        }

下面的效果图是在画布移动之前没有使用canvas.save()保存状态和使用
canvas不只是显示自己画的,也可以显示我们的资源图片,canvas里面有一个drawBitmap方法,我们可以通过canvas.drawBitmap在我们设置的位置展示图片
这里写图片描述
示例代码

Bitmap bitmap=null;                  bitmap=((BitmapDrawable)getResources().getDrawable(R.drawable.ubuntu)).getBitmap();canvas.drawBitmap(bitmap, 0, 0, null); bitmap=((BitmapDrawable)getResources().getDrawable(R.drawable.timer)).getBitmap();canvas.drawBitmap(bitmap, 50, 50, null);

如何让时钟转动起来很简单,就是在原来的时钟基础上面再重新画出时分秒针。然后设置一个定时器就可以了。
这里写图片描述
实现代码如下:

package demo.liuyongxiang.com.demo.activities;import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.RectF;import android.graphics.drawable.BitmapDrawable;import android.os.Bundle;import android.os.Handler;import android.view.View;import java.util.Calendar;import demo.liuyongxiang.com.demo.R;/** * Created by ytx on 2016/11/10. */public class MyCanvas extends Activity {    private Paint mPaint;    private float mCenterX;    private float mHourLength;    private float mMinuteLength;    private float mSecondLength;    private Handler handler = new Handler();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(new CustomView1(MyCanvas.this));    }    class CustomView1 extends View implements Runnable{        Paint paint;        public CustomView1(Context context) {            super(context);            paint = new Paint(); //设置一个笔刷大小是3的黄色的画笔            paint.setColor(Color.RED);            handler.postDelayed(this, 1000);        }        //在这里我们将测试canvas提供的绘制图形方法        @Override        protected void onDraw(Canvas canvas) {            paint.setStrokeWidth(5);            paint.setStyle(Paint.Style.STROKE);            paint.setAntiAlias(true);            paint.setStyle(Paint.Style.STROKE);            //将要画的位置移动到屏幕中间            canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);            //将位置移动画纸的坐标点:150,150            //以半径为150和180画圆            canvas.drawCircle(0, 0, 150, paint);            canvas.drawCircle(0, 0, 180, paint);            //使用path绘制路径文字            canvas.save();            //移动绘制文字的位置            canvas.translate(0, 0);            Path path = new Path();            //绘制的时候要注意左上不能大于右下,否则不会显示            RectF rect = new RectF(-100,-100,100,100);            path.addArc(rect, -220, 280);            Paint citePaint = new Paint(paint);            citePaint.setTextSize(28);            //设置画笔的粗细            citePaint.setStrokeWidth(3);            //float hOffset, float vOffset// 设置水平位置  vOffset  设置垂直位置            // 如果hOffset为0 说明开始位置在path.addArc设置的startAngle开始角度            // 如果vOffset 为0说明经过的位置是在与RectF的顶部相切处            canvas.drawTextOnPath("http://blog.csdn.net/u014452224", path, 14, 0, citePaint);            //为了方便一些转换操作,Canvas 还提供了保存和回滚属性的方法(save和restore),            // 比如你可以先保存目前画纸的位置(save),            // 然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置            canvas.restore();            Paint smallPaint = new Paint(paint); //非数字刻度画笔对象            smallPaint.setStrokeWidth(2);            smallPaint.setColor(Color.GRAY);            float  y=150;            int count = 60; //总刻度数            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);            paint.setStrokeWidth(6);            paint.setColor(Color.RED);            paint.setTextSize(24);            paint.setStrokeWidth(3);            for(int i=0 ; i <count ; i++){                if(i%5 == 0){                    //绘制数字刻度                    canvas.drawText(i == 0 ? "12" : String.valueOf(i / 5),((i / 5)>9||i==0)?-15f:-6f , -y-5f, paint);                }else{                    //绘制非数字的刻度                    canvas.drawLine(0f, y, 0f, y +15f, smallPaint);                }                canvas.rotate(360/count,0f,0f); //旋转画纸            }            Calendar calendar = Calendar.getInstance();            int currentMinute = calendar.get(Calendar.MINUTE);            int currentHour = calendar.get(Calendar.HOUR);            int currentSecond = calendar.get(Calendar.SECOND);            // 计算分针和时间的角度            double secondRadian = Math.toRadians((360 - ((currentSecond * 6) - 90)) % 360);            double minuteRadian = Math.toRadians((360 - ((currentMinute * 6) - 90)) % 360);            double hourRadian = Math.toRadians((360 - ((currentHour * 30) - 90))% 360 - (30 * currentMinute / 60));            // 设置实针为6个象素粗            paint.setStrokeWidth(6);            // 在表盘上画时针            mCenterX = 0;            mHourLength = 100;            canvas.drawLine(mCenterX, mCenterX,                    (int) (mCenterX + mHourLength * Math.cos(hourRadian)),                    (int) (mCenterX - mHourLength * Math.sin(hourRadian)), paint);            // 设置分针为4个象素粗            paint.setStrokeWidth(4);            mMinuteLength = 120;            // 在表盘上画分针            canvas.drawLine(mCenterX, mCenterX,                    (int) (mCenterX + mMinuteLength* Math.cos(minuteRadian)),                    (int) (mCenterX - mMinuteLength* Math.sin(minuteRadian)),                    paint);            // 设置分针为2个象素粗            paint.setStrokeWidth(2);            // 在表盘上画秒针            mSecondLength = 145;            int centerY = 30;            canvas.drawLine((int) (mCenterX - centerY* Math.cos(secondRadian)),(int) (mCenterX + centerY* Math.sin(secondRadian)),                    (int) (mCenterX + mSecondLength* Math.cos(secondRadian)),                    (int) (mCenterX - mSecondLength* Math.sin(secondRadian)),                    paint);            paint.setStyle(Paint.Style.FILL);            canvas.drawCircle(0, 0, 5, paint);    }        @Override        public void run() {            // 重新绘制View            this.invalidate();            // 重新设置定时器,在60秒后调用run方法            handler.postDelayed(this, 1000);        }    }}

通过效果图和代码我们可以知道,我们设置的坐标点其实就是我们所要展示的图片的左上角的顶点。
如果你觉得这篇博客对你有帮助请给予好评,如有疑问请留言。

1 0