图形绘制之——Canvas详解(一)

参照一个大神的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);
            }
        });

    }


}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值