自定义view相关

Canvas高级使用以实际操作

2019-11-07  本文已影响0人  大苏打6815

Paint画笔大家一般都知道,就是画笔,设置一些样式,粗细,颜色等等属性。
Canvas字面翻译就是画布,本质其实是一个绘制图形的工具类。
注意:canvas不是具体执行绘制的对象,具体执行的是GPU

坐标体系:

默认的坐标零点位于屏幕左上角
向下为Y轴正方向,向右为X轴正方向

image.png

canvas坐标体系一经确定不会改变的。
绘制坐标系:特性在修改坐标后,这个过程中是不可逆的。

主要方法

1、Canvas.save()
可以理解为保存画布,作用是将之前的所有已绘制的图像保存起来,让后续的操作就好像在一个新的图层上操作一样
2、Canvas.restore()
可以理解为合并图层操作,作用是将save()之后绘制的所有图像与sava()之间的图像进行合并
3、Canvas.translate()
绘图坐标的平移
4、Canvas.rotare()
绘图坐标的翻转

Matrix变换矩阵运算方式
image.png

scale是缩放,skew是错切,trans是平移,persp代表透视。
rotare翻转则是改变的是scale,skew,trans三个的值。

image.png

Canvas里面牵扯两种坐标系:Canvas自己的坐标系、绘图坐标系

Canvas的坐标系:
它就在View的左上角,做坐标原点往右是X轴正半轴,往下是Y轴的正半轴,有且只有一个,唯一不变

绘图坐标系:
它不是唯一不变的,它与Canvas的Matrix有关系,当Matrix发生改变的时候,绘图坐标系对应的进行改变, 同时这个过程是不可逆的(save和restore方法来保存和还原变化操作), Matrix又是通过我们设置translate、rotate、scale、skew来进行改变的

Canvas的状态保存---状态栈、Layer栈

状态栈--save、 restore方法来保存和还原变换操作Matrix以及Clip剪裁,也可以通过restoretoCount直接还原到对应栈的保存状态.

比如你用画笔工具画东西的时候,本来就有一层,你调用一层sava,就有两层,在调用一次sava就有三层,也就是说调用两次sava,有三层,从低往高的形式。那restore可以理解为出栈的方式。调用restore之后会出栈,把容器里绘制的东西都会整合到一起。还有个restoreacount方法。比如说容器里面有三层,理解为三个栈,然后调用restoreaccount(2),首先会把容器里面所有东西都组合起来,保存起来,然后第三层的时候就出栈。

Layer栈:saveLayer的时候都会新建一个透明的图层(离屏Bitmap-离屏缓冲),并且会将saveLayer之前的一些Canvas操作延续过来, 后续的绘图操作都在新建的layer上面进行, 当我们调用restore 或者 restoreToCount 时 更新到对应的图层和画布上.

sava和savaLayer差不多,只不过sava是一个不透明的图层,savaLayer是一个透明的图层。

以下有两个例子;分别是刮刮卡还有画一个钟表的例子
刮刮卡例子:

public class GuaGuaCardView_SRCOUT extends View {
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC,BmpText;
    private Path mPath;
    private float mPreX,mPreY;
    public GuaGuaCardView_SRCOUT(Context context) {
        super(context);


        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        mBitPaint.setColor(Color.RED);
        mBitPaint.setStyle(Paint.Style.STROKE);
        mBitPaint.setStrokeWidth(45);

        BmpText = BitmapFactory.decodeResource(getResources(), R.drawable.guaguaka_text1,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(), R.drawable.guaguaka,null);
        BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
        mPath = new Path();
    }
    public GuaGuaCardView_SRCOUT(Context context, AttributeSet attrs) {
        super(context, attrs);

        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        mBitPaint.setColor(Color.RED);
        mBitPaint.setStyle(Paint.Style.STROKE);
        mBitPaint.setStrokeWidth(45);

        BmpText = BitmapFactory.decodeResource(getResources(), R.drawable.guaguaka_text1,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(), R.drawable.guaguaka,null);
        BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
        mPath = new Path();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawBitmap(BmpText,0,0,mBitPaint);
//        canvas.save();
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        //先把手指轨迹画到目标Bitmap上
        Canvas c = new Canvas(BmpDST);
        c.drawPath(mPath,mBitPaint);

        //然后把目标图像画到画布上
        canvas.drawBitmap(BmpDST,0,0,mBitPaint);

        //计算源图像区域
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(2);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(),event.getY());
                mPreX = event.getX();
                mPreY = event.getY();
                return true;
            case MotionEvent.ACTION_MOVE:
                float endX = (mPreX+event.getX())/2;
                float endY = (mPreY+event.getY())/2;
                mPath.quadTo(mPreX,mPreY,endX,endY);
                mPreX = event.getX();
                mPreY =event.getY();
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        postInvalidate();
        return super.onTouchEvent(event);
    }
}

钟表例子:

public class WatchView extends View {
    private static final String TAG = "WatchView";
    private int mWidth;
    private int mHerght;


    public WatchView(Context context) {
        super(context);
    }

    public WatchView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public WatchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHerght = getMeasuredHeight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画外圆
        Paint paintCircle = new Paint();
        paintCircle.setStrokeWidth(5);
        paintCircle.setAntiAlias(true);
        paintCircle.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(mWidth / 2,
                mHerght / 2, mWidth / 2, paintCircle);

        //画刻度线
        Paint paintDegree = new Paint();
        paintCircle.setStrokeWidth(3);
        for (int i = 0; i < 12; i++) {
            if (i == 0 || i == 3 || i == 6 || i == 9) {
                paintDegree.setStrokeWidth(5);
                paintDegree.setTextSize(100);
                canvas.drawLine(mWidth / 2, mHerght / 2 - mWidth / 2,
                        mWidth / 2, mHerght / 2 - mWidth / 2 + 60,
                        paintDegree);
                String degree = String.valueOf(i);
                canvas.drawText(degree, mWidth / 2 - paintDegree.measureText(degree) / 2,
                        mHerght / 2 - mWidth / 2 + 160, paintDegree);
            } else {
                paintDegree.setStrokeWidth(3);
                paintDegree.setTextSize(50);
                canvas.drawLine(mWidth / 2, mHerght / 2 - mWidth / 2,
                        mWidth / 2, mHerght / 2 - mWidth / 2 + 30,
                        paintDegree);
                String degree = String.valueOf(i);
                canvas.drawText(degree, mWidth / 2 - paintDegree.measureText(degree) / 2,
                        mHerght / 2 - mWidth / 2 + 60, paintDegree);
            }
            canvas.rotate(30, mWidth / 2, mHerght / 2);
        }
        //画指针
        Paint paintHour = new Paint();
        paintHour.setStrokeWidth(20);
        Paint paintMinute = new Paint();
        paintMinute.setStrokeWidth(10);
//        Log.i(TAG, "Current SaveCount1 = " + canvas.getSaveCount());
//        canvas.save();
//        Log.i(TAG, "Current SaveCount2 = " + canvas.getSaveCount());
        canvas.translate(mWidth/2, mHerght/2);
        canvas.drawLine(0,0,100,100,paintHour);
        canvas.drawLine(0,0,100,200,paintMinute);
//        canvas.restore();
        Log.i(TAG, "Current SaveCount3 = " + canvas.getSaveCount());
    }
}
上一篇下一篇

猜你喜欢

热点阅读