Android Canvas、Paint、Path、drawBitmap

我们平常的画图都是通过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时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。我们先来实现一下
这里写图片描述
示例代码为

@Override
protected 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
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值