Canvrs自由绘图

来源:互联网 发布:下一次金融危机 知乎 编辑:程序博客网 时间:2024/04/29 23:34
因为项目需要,所以前段时间做了个绘图工具,不是很完美,下来展示一下效果图:

这里写图片描述

功能介绍

 对于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

原创粉丝点击