美文网首页自定义view相关
Canvas高级使用以实际操作

Canvas高级使用以实际操作

作者: 大苏打6815 | 来源:发表于2019-11-07 11:23 被阅读0次

    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());
        }
    }
    

    相关文章

      网友评论

        本文标题:Canvas高级使用以实际操作

        本文链接:https://www.haomeiwen.com/subject/jnjbvctx.html