您的位置:首页 > 其它

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

2015-09-16 14:53 197 查看
参照一个大神的Canvas介绍:/article/1390946.html

今天我们来学习一下如何用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);
            }
        });

    }

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: