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);// 绘图的起始点 } break; case 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
阅读全文
1 0
- Canvrs自由绘图
- CButton重绘图片实现自由缩放和拖动
- Java在JPanel中自由绘图,并将绘图保存为jpg文件
- 自由
- 自由
- 自由
- 自由
- 自由
- 自由
- 自由
- 自由
- 自由
- 自由
- 自由~~~
- ios 自由批注功能的实现 如何实现在view上自由绘图 bitmap Quartz 2D 缓存位图
- IBM:gnuplot 让您的数据可视化 自由控制高级图表和数据绘图
- ios 画图 绘图的实现(自由批注中视图的subview的实现)bitmap Quartz 2D 缓存位图 自由批注功能的实现 如何实现在view上自由绘图 提供源码
- 绘图
- NIO之二(Buffer&&Selector)
- 使用python代码找到Python site-packages目录位置
- window下cmd命令共享文件夹
- 23-事件
- Excuses, Excuses!
- Canvrs自由绘图
- Java遍历文件夹&判断是否存在某一类型的文件
- Rxjava中的ConnectableObservable
- 【转】设备树的用法(Device Tree Usage)
- 1003. 我要通过!(20)
- 项目整合Activiti关联业务(二)
- Java socket 访问网页
- linux运行级别
- 概率分析和随机算法