Canvrs自由绘图

因为项目需要,所以前段时间做了个绘图工具,不是很完美,下来展示一下效果图:

这里写图片描述

功能介绍

 对于Canvrs就不用我多做介绍了,估计大家能来看这篇文章,也对Canvrs有了基本的认识,
 不认识的朋友可以百度一下,网上有很多这种介绍。我就直接说下本位实现的功能
 以及列出一些常用的属性。
  • 自由绘制划线,画箭头
  • 橡皮察
  • 文字描述,可以设置字体大小,字体颜色
  • 绘制图片,可以双指旋转,放大、放小、单指随意拖动
  • 撤销划线,返回上一步。
  • 添加背景
  • 保存成图片存入本地
  • 可以选择操作某个图片

    说道画布,肯定是需要手势操作了,在这里列举画布常用的属性及手势操
    
  • setARGB(int a,int r,int g,int b):设置ARGB颜色

  • setColor(int color):设置颜色。
  • setAlpha(int a):设置透明度。
  • setPathEffect(PathEffect effect):设置绘制路径时的路径效果。
  • setShader(Shader shader):设置Paint的填充效果。
  • setAntiAlias(boolean aa):设置是否抗锯齿。
  • setStrokeWidth(float width):设置Paint的笔触宽度。
  • setStyle(Paint.Style style):设置Paint的填充风格。
  • setTextSize(float textSize):设置绘制文本时的文字大小。
手势操作
  • MotionEvent.ACTION_DOWN:手指触摸屏幕。
  • MotionEvent.ACTION_MOVE:手指移动。
  • MotionEvent.ACTION_UP:手指离开屏幕。
  • MotionEvent.ACTION_POINTER_DOWN:双指按下
  • MotionEvent.ACTION_POINTER_UP:双指抬起
自由绘制划线,画箭头
 因为要做撤销功能,所以会建个对象集合,用来保存每步的操作,每次会根据手指
的抬起或者按下时的坐标来存入path,和paint到集合。而划线是根据手指划过的
路径来绘制点,连起来就是一条线,你可以设置直线,或者是虚线,因项目需要,
我用的是虚线。下面给大家贴出代码
  //虚线的画笔
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);// 设置画笔的默认颜色
        mPaint.setStyle(Paint.Style.STROKE);// 设置画笔的填充方式为无填充、仅仅是画线
        mPaint.setStrokeWidth(5);// 设置画笔的宽度为1
        PathEffect effects = new DashPathEffect(new float[]{5,5,5,5},1);
        mPaint.setPathEffect(effects);

在onDraw方法里如下:

 if (path != null) {
            canvas.drawPath(path, mPaint);  // 实时的显示
        }

在onTouchEvent方法里如下

@Override
    public boolean onTouchEvent(MotionEvent event) {
        // 获取触摸位置
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction() & MotionEvent.ACTION_MASK){// 获取触摸的各个瞬间
            case MotionEvent.ACTION_DOWN:// 手势按下
                path=new Path();
                // 手指摁下时清空之前的设置
                dp = new DrawPathDTO();
                dp.paint=mPaint;
                dp.path=path;
                path.moveTo(x,y);// 绘图的起始点
              }
                breakcase MotionEvent.ACTION_MOVE:  //手势移动
                float dx = Math.abs(x - preX);
                float dy = Math.abs(y - preY);
                if (dx > 5 || dy > 5) {// 用户要移动超过5像素才算是画图,免得手滑、手抖现象

                        path.quadTo(preX, preY, (x + preX) / 2, (y + preY) / 2);
                        preX = x;
                        preY = y;
                    cacheCanvas.drawPath(path, mPaint);// 绘制路径
             }
                break;
            case MotionEvent.ACTION_UP://手势抬起
                path.lineTo(preX,preY);
                path = null;// 重新置空
                savePath.add(dp)
                break;
        }
        invalidate();
        return true;
    }
 画箭头的方法,画箭头这个东西太麻烦啦,开始想用把箭头画好,然后到指定点旋转的方法但是,
 效果一直不好。想用数学的方法来画,但是发现计算很复杂啊。于是google,
 发现一个兄台使用了Java当中的awt实现了画箭头(http://www.bangchui.org/simple/?t16755.html),于是就借过来,改了一下。
 /** 
     * 画箭头 
     * @param sx 
     * @param sy 
     * @param ex 
     * @param ey 
     */  
    public void drawAL(int sx, int sy, int ex, int ey)  
    {  
        double H = 8; // 箭头高度     
        double L = 3.5; // 底边的一半     
        int x3 = 0;  
        int y3 = 0;  
        int x4 = 0;  
        int y4 = 0;  
        double awrad = Math.atan(L / H); // 箭头角度     
        double arraow_len = Math.sqrt(L * L + H * H); // 箭头的长度     
        double[] arrXY_1 = rotateVec(ex - sx, ey - sy, awrad, true, arraow_len);  
        double[] arrXY_2 = rotateVec(ex - sx, ey - sy, -awrad, true, arraow_len);  
        double x_3 = ex - arrXY_1[0]; // (x3,y3)是第一端点     
        double y_3 = ey - arrXY_1[1];  
        double x_4 = ex - arrXY_2[0]; // (x4,y4)是第二端点     
        double y_4 = ey - arrXY_2[1];  
        Double X3 = new Double(x_3);  
        x3 = X3.intValue();  
        Double Y3 = new Double(y_3);  
        y3 = Y3.intValue();  
        Double X4 = new Double(x_4);  
        x4 = X4.intValue();  
        Double Y4 = new Double(y_4);  
        y4 = Y4.intValue();  
        // 画线     
        myCanvas.drawLine(sx, sy, ex, ey,myPaint);  
        Path triangle = new Path();  
        triangle.moveTo(ex, ey);  
        triangle.lineTo(x3, y3);    
        triangle.lineTo(x4, y4);   
        triangle.close();  
        myCanvas.drawPath(triangle,myPaint);  

    }  
    // 计算     
    public double[] rotateVec(int px, int py, double ang, boolean isChLen, double newLen)  
    {  
        double mathstr[] = new double[2];  
        // 矢量旋转函数,参数含义分别是x分量、y分量、旋转角、是否改变长度、新长度     
        double vx = px * Math.cos(ang) - py * Math.sin(ang);  
        double vy = px * Math.sin(ang) + py * Math.cos(ang);  
        if (isChLen) {  
            double d = Math.sqrt(vx * vx + vy * vy);  
            vx = vx / d * newLen;  
            vy = vy / d * newLen;  
            mathstr[0] = vx;  
            mathstr[1] = vy;  
        }  
        return mathstr;  
    }  
橡皮察
  橡皮察的主要路,就是讲画笔的颜色改成跟画布一样的颜色,在吧画笔放大,就可以。没什么说的。
(有点投机取巧),哪位大神有新方法,也可分享下。
文字描述,可以设置字体大小,字体颜色
 本来想做成跟WS一样自带可以拖动,移动,旋转的那种文本样式,结果想不到思路,
要不就是思路,用代码是现实不了,要不就是太麻烦。就写成了,单击屏幕获得坐标,
弹出对话框的形式,吧文本绘制的画布上。这个做的确实不满意,之后想到好思路再改吧,
哪位大神有想法希望可以跟小弟交流一下,感激不尽。
绘制图片,可以双指旋转,放大、放小、单指随意拖动
 图片的绘制,是一定要保存每张图片的对象,图片分装成了Bitmap位图显示在画布上,
每次绘制的时候,都要根据保存的集合对象,全部绘制一遍,包括移动,旋转。还有之后选择
操作某张图片。主要代码在触摸事件里,我给出,我的触摸事件的所有代码,包括划线的。
 @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 获取触摸位置
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction() & MotionEvent.ACTION_MASK){// 获取触摸的各个瞬间
            case MotionEvent.ACTION_DOWN:// 手势按下
                path=new Path();
                // 手指摁下时清空之前的设置
                dp = new DrawPathDTO();
                dp.paint=mPaint;
                dp.path=path;
                path.moveTo(x,y);// 绘图的起始点
                if (mtype == 1) {
                    if (saveBitmap.size()>0&&saveBitmap!=null) {
                        Iterator<DrawBitmapDTO> iter = saveBitmap.iterator();
                        while (iter.hasNext()) {
                            DrawBitmapDTO drawbitmap = iter.next();
                            if (drawbitmap.isStamp){
                                mode = DRAG;
                                x_down = x;
                                y_down = y;
                                if (drawbitmap.isinit){
                                drawbitmap.matrix.postTranslate(x_down,y_down);//定位初始位置
                                    drawbitmap.isinit=false;
                                }else{
                                    drawbitmap.matrix.postTranslate(x_down-drawbitmap.X_last,y_down-drawbitmap.Y_last);//定位初始位置
                                }
                                savedMatrix.set(drawbitmap.matrix);
                                drawbitmap.X_last=x;drawbitmap.Y_last=y;
                            }
                        }
                    }

                }
                textX=x;
                textY=y;
                preX = x;
                preY = y;
                startX=event.getX();
                startY=event.getY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if (saveBitmap.size()>0&&saveBitmap!=null) {
                    Iterator<DrawBitmapDTO> iter = saveBitmap.iterator();
                    while (iter.hasNext()) {
                        DrawBitmapDTO drawbitmap = iter.next();
                        if (drawbitmap.isStamp){
                            mode = ZOOM;
                            oldDist = spacing(event);   //触碰两点间的距离
                            oldRotation = rotation(event);  //计算旋转角度
                            savedMatrix.set(drawbitmap.matrix);
                            midPoint(mid, event);
                        }
                    }
                }

                break;
            case MotionEvent.ACTION_MOVE:  //手势移动
                float dx = Math.abs(x - preX);
                float dy = Math.abs(y - preY);
                if (dx > 5 || dy > 5) {// 用户要移动超过5像素才算是画图,免得手滑、手抖现象
                if (mtype==0){
                        path.quadTo(preX, preY, (x + preX) / 2, (y + preY) / 2);
                        preX = x;
                        preY = y;
                    cacheCanvas.drawPath(path, mPaint);// 绘制路径
                }
                if (mtype==1){
                        Iterator<DrawBitmapDTO> iter = saveBitmap.iterator();
                        while (iter.hasNext()) {
                            DrawBitmapDTO drawbitmap = iter.next();
                            if (drawbitmap.isStamp){
                                if (mode == ZOOM) {
                                    matrix1.set(savedMatrix);
                                    float rotation = rotation(event) - oldRotation;
                                    float newDist = spacing(event);
                                    float scale = newDist / oldDist;
                                    matrix1.postScale(scale, scale, mid.x, mid.y);// 縮放
                                    matrix1.postRotate(rotation, mid.x, mid.y);// 旋轉
                                    matrixCheck = matrixCheck();
                                    if (matrixCheck == false) {
                                        drawbitmap.matrix.set(matrix1);
                                        invalidate();
                                    }
                                } else if (mode == DRAG) {
                                    matrix1.set(savedMatrix);
                                    matrix1.postTranslate(event.getX() - x_down, event.getY()
                                            - y_down);// 平移
                                    matrixCheck = matrixCheck();  //检查是否超出屏幕外
                                    matrixCheck = matrixCheck();
                                    if (matrixCheck == false) {
                                        drawbitmap.matrix.set(matrix1);
                                        invalidate();
                                    }
                                }
                            }
                        }
                }

                }
                break;
            case MotionEvent.ACTION_UP://手势抬起
                path.lineTo(preX,preY);
                cacheCanvas.drawPath(path, mPaint);  // 实时的显示
                path = null;// 重新置空
                savePath.add(dp);
                if (mUp != null&&mtype==2) {
                    mUp.OnUp(x, y);
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
        }
        invalidate();
        return true;
    }

//判断是超出界面,当然也可不判断
  private boolean matrixCheck() {
        if (saveBitmap.size()>0&&saveBitmap!=null) {
            Iterator<DrawBitmapDTO> iter = saveBitmap.iterator();
            while (iter.hasNext()) {
                DrawBitmapDTO drawbitmap = iter.next();
                if (drawbitmap.isStamp) {
                    float[] f = new float[9];
                    matrix1.getValues(f);
                    // 图片4个顶点的坐标
                    float x1 = f[0] * 0 + f[1] * 0 + f[2];
                    float y1 = f[3] * 0 + f[4] * 0 + f[5];
                    float x2 = f[0] * drawbitmap.imageBitmap.getWidth() + f[1] * 0 + f[2];
                    float y2 = f[3] * drawbitmap.imageBitmap.getWidth() + f[4] * 0 + f[5];
                    float x3 = f[0] * 0 + f[1] * drawbitmap.imageBitmap.getHeight() + f[2];
                    float y3 = f[3] * 0 + f[4] * drawbitmap.imageBitmap.getHeight() + f[5];
                    float x4 = f[0] * drawbitmap.imageBitmap.getWidth() + f[1] * drawbitmap.imageBitmap.getHeight() + f[2];
                    float y4 = f[3] * drawbitmap.imageBitmap.getWidth() + f[4] * drawbitmap.imageBitmap.getHeight() + f[5];
                }
            }
        }
        return false;
    }

   // 触碰两点间距离
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return  (float)Math.sqrt(x * x + y * y);//FloatMath..sqrt(x * x + y * y);
    }

    // 取手势中心点
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }

    // 取旋转角度
    private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);
        return (float) Math.toDegrees(radians);
    }
撤销划线,返回上一步。
 这一步主要就是对之前划线,画图对象集合的操作,每次删掉集合最后一位,在从新绘制集合,
 下面给大家上代码,我只是撤销掉了划线的集合。  如果大家需要,也可以自己加上撤销图片的集合
   /**
     * 撤销上一步操作
     */
    public void undo() {
 //TODO Auto-generated method stub
        if (this.cacheBitmap != null) {
            cacheBitmap = Bitmap.createBitmap(view_width, view_height, Bitmap.Config.ARGB_8888);
            cacheCanvas.setBitmap(cacheBitmap);
        }
        if (savePath != null && savePath.size() > 0) {
            // 移除最后一个path,相当于出栈操作
            savePath.remove(savePath.size() - 1);
            Iterator<DrawPathDTO> iter = savePath.iterator();
            while (iter.hasNext()) {
                DrawPathDTO drawPath = iter.next();
                cacheCanvas.drawPath(drawPath.path, drawPath.paint);
            }
            }
            invalidate();// 刷新
    }
保存成图片存入本地
这个就不做叙述了,直接给大家上代码
 //保存图片
    public void saveBitmap(){
        Bitmap b=BitmapFactory.decodeResource(getResources(), R.mipmap.bg);
        Bitmap bitmap = Bitmap.createBitmap(view_width, view_height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawBitmap(b, 0, 0, null);
        canvas.drawBitmap(cacheBitmap, 0, 0, mPaint);// 把cacheBitmap画到DrawView上
        if (saveBitmap.size()>0&&saveBitmap!=null){
            Iterator<DrawBitmapDTO> iter = saveBitmap.iterator();
            while (iter.hasNext()) {
                DrawBitmapDTO drawbitmap = iter.next();
                canvas.drawBitmap(drawbitmap.imageBitmap, drawbitmap.matrix, mPaint);
            }
        }
        //保存全部图层
        canvas.save(Canvas.ALL_SAVE_FLAG);
        canvas.restore();
        //存储路径
        File file = new File("/sdcard/song/");
        if(!file.exists())
            file.mkdirs();
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file.getPath() + "/ty2017.jpg");
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);

            fileOutputStream.close();
            //弹出吐司,提醒用户图片的保存路径
            Toast.makeText(getContext(),"图像已保存",
                    Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
其实主要的重点就是这些了,希望能帮到大家,哪里写的不好的地方,请大家勿怪……。下面将源码的链接给你们。

http://download.csdn.net/detail/changalbert/9912076

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值