因为要保存图片,所以先添加权限。

设计自定义的view:DevinDrawPanle,设计很多的图形和提供调用的方法,所代码比较多

/*** 自定义绘图面板* Created by Devin Chen on 2016/12/22.*/public class DevinDrawPanle extends View {private Canvas mCanvas;//画布private Paint mPaint;//画笔private Paint mBitmapPaint;//专门绘制图片的画笔;private Path mPath;//手绘路径private Bitmap mBitmap;//图片private Bitmap mTempBitmap;//临时图片private int shapeStyle = 0;//形状private int mWidth, mHeight;//宽高private float drawX, drawY;//绘制点//绘制幅度,当一个方向上触摸移动等于这个距离就绘制一条线,quadTo是绘制贝塞尔曲线private final static int TOUCH_TOLERANCE = 4;public DevinDrawPanle(Context context) {super(context);initView();}public DevinDrawPanle(Context context, AttributeSet attrs) {super(context, attrs);initView();}public DevinDrawPanle(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initView();}/*** 初始化*/private void initView() {initPaints();//初始化画笔mPath = new Path();//初始化绘图路径}/*** 初始化画笔*/private void initPaints() {//绘图画笔mPaint = new Paint();//用数组管理画笔mPaint.setAntiAlias(true);mPaint.setColor(Color.RED);mPaint.setStrokeWidth(1);mPaint.setDither(true);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeJoin(Paint.Join.ROUND);mPaint.setStrokeCap(Paint.Cap.ROUND);//绘制图片的画笔mBitmapPaint = new Paint(Paint.DITHER_FLAG);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mWidth = w;mHeight = h;mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);mCanvas = new Canvas(mBitmap);//绘制背景mCanvas.drawColor(Color.WHITE);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);//使用这一句,每次调用都把画好的图片在此绘制一遍,可保留绘画痕迹canvas.drawPath(mPath, mPaint);//不断被调用,不断绘制}/*** 触摸事件** @param event* @return*/@Overridepublic boolean onTouchEvent(MotionEvent event) {float x = event.getX();float y = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN://按下touchStart(x, y);break;case MotionEvent.ACTION_MOVE://移动touchMove(x, y);break;case MotionEvent.ACTION_UP://提起touchEnd(x, y);break;default:break;}invalidate();//刷新视图return true;}/*** 绘制结束** @param x* @param y*/private void touchEnd(float x, float y) {switch (shapeStyle) {case 0:drawBezierEnd(x, y);//绘制贝塞尔曲线结束break;case 1:drawLine(x, y);break;case 2:drawRect(x, y);break;case 3:drawCircle(x, y);break;case 4:drawOval(x, y);break;case 5:drawRoundRect(x, y);break;default:break;}}/*** 绘制贝塞尔曲线结束** @param x* @param y*/private void drawBezierEnd(float x, float y) {mPath.lineTo(x, y);mCanvas.drawPath(mPath, mPaint);//这句必须要,否则绘制的东西立即消失mPath.reset();}/*** 绘制path** @param x* @param y*/private void touchMove(float x, float y) {//装载临时图片,不抬起就不会真正画到画布上,画贝塞尔除外mCanvas.drawBitmap(mTempBitmap, 0, 0, mBitmapPaint);switch (shapeStyle) {case 0:drawBezier(x, y);//绘制贝塞尔曲线break;case 1:drawLine(x, y);//绘制直线break;case 2:drawRect(x, y);//绘制矩形break;case 3:drawCircle(x, y);//绘制圆break;case 4:drawOval(x, y);//绘制椭圆break;case 5:drawRoundRect(x, y);//绘制圆角矩形,圆角12break;default:break;}}/*** 绘制直线** @param x* @param y*/private void drawLine(float x, float y) {mCanvas.drawLine(drawX, drawY, x, y, mPaint);}/*** 绘制矩形** @param x* @param y*/private void drawRect(float x, float y) {mCanvas.drawRect(drawX, drawY, x, y, mPaint);}/*** 绘制圆** @param x* @param y*/private void drawCircle(float x, float y) {//求得圆心位置float cx = (drawX + x) / 2;float cy = (drawY + y) / 2;//求得半径float radius = (float) Math.sqrt(Math.pow(x - drawX, 2) + Math.pow(y - drawY, 2)) / 2;//半径等于对角线的一半mCanvas.drawCircle(cx, cy, radius, mPaint);}/*** 绘制椭圆** @param x* @param y*/private void drawOval(float x, float y) {RectF rectF = new RectF(drawX, drawY, x, y);mCanvas.drawOval(rectF, mPaint);}/*** 绘制圆角矩形** @param x* @param y*/private void drawRoundRect(float x, float y) {RectF rectF = new RectF(drawX, drawY, x, y);mCanvas.drawRoundRect(rectF, 12, 12, mPaint);}/*** 绘制贝塞尔曲线** @param x* @param y*/private void drawBezier(float x, float y) {float dx = Math.abs(x - drawX);float dy = Math.abs(y - drawY);//计算绘制距离if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {//绘制一阶贝赛尔曲线,前两个参数为控制点,后两个为结束点mPath.quadTo((x + drawX) / 2, (y + drawY) / 2, x, y);//这里以中间位置为控制点drawX = x;drawY = y;//重新记录绘画点}}/*** 绘制开始** @param x* @param y*/private void touchStart(float x, float y) {mPath.reset();//路径工具复位mPath.moveTo(x, y);//绘制点移动drawX = x;drawY = y;//记录下来,作为正式绘制的起始点mTempBitmap = Bitmap.createBitmap(mBitmap);//保存临时图片}public void saveBitmap(File file) {//如果文件夹不存在则创建if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}//如果文件不存在则创建if (!file.exists()) {try {file.createNewFile();} catch (IOException e) {e.printStackTrace();}}OutputStream outputStream = null;BufferedOutputStream bos=null;try {outputStream = new FileOutputStream(file);//输出流bos = new BufferedOutputStream(outputStream);//缓冲输出流mBitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);//压缩图像到输出流bos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (bos != null) {try {bos.close();} catch (IOException e) {e.printStackTrace();}}if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 设置画笔颜色** @param color*/public void setPaintColor(@ColorInt int color) {mPaint.setColor(color);}/*** 设置画笔颜色** @param colorID 从资源文件中获取*/public void setPaintColorFromResources(@ColorRes int colorID) {mPaint.setColor(getResources().getColor(colorID));}/*** 设置画笔颜色** @param color 从字符串解析*/public void setPaintColor(String color) {try {//有可能解析失败mPaint.setColor(Color.parseColor(color));} catch (Exception e) {e.printStackTrace();}}/*** 设置画笔粗细** @param width*/public void setPaintStrokeWidth(float width) {mPaint.setStrokeWidth(width);}/*** 画矩形** @param left* @param top* @param right* @param bottom*/public void drawRect(float left, float top, float right, float bottom) {mCanvas.drawRect(left, top, right, bottom, mPaint);}/*** 获得画布** @return*/public Canvas getCanvas() {return mCanvas;}/*** 获得画笔** @return*/public Paint getPaint() {return mPaint;}/*** 设置绘制形状** @param shapeStyle*/public void setShapeStyle(int shapeStyle) {this.shapeStyle = shapeStyle;}
}

这样就可以调用了。

设计菜单文件:

1.menu_main_option.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:id="@+id/menu_item_save"android:title="保存" /></menu>

2.menu_paint_colors.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" ><item android:id="@+id/color_red" android:title="红色"/><item android:id="@+id/color_orange" android:title="橙色"/><item android:id="@+id/color_yellow" android:title="黄色"/><item android:id="@+id/color_green" android:title="绿色"/><item android:id="@+id/color_cyan" android:title="青色"/><item android:id="@+id/color_blue" android:title="蓝色"/><item android:id="@+id/color_purple" android:title="紫色"/><item android:id="@+id/color_black" android:title="黑色"/><item android:id="@+id/color_white" android:title="白色"/><item android:id="@+id/color_gray" android:title="灰色"/>
</menu>

3.menu_line_style.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" ><item android:id="@+id/drawline1" android:title="1"/><item android:id="@+id/drawline2" android:title="2"/><item android:id="@+id/drawline3" android:title="3"/><item android:id="@+id/drawline4" android:title="4"/><item android:id="@+id/drawline5" android:title="5"/>
</menu>

4.menu_shape_style.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" ><item android:id="@+id/drawbezier" android:title="自由线"/><item android:id="@+id/drawline" android:title="直线"/><item android:id="@+id/drawrect" android:title="矩形"/><item android:id="@+id/drawcircle" android:title="圆形"/><item android:id="@+id/drawoval" android:title="椭圆"/><item android:id="@+id/drawroundrect" android:title="圆角矩形"/>
</menu>

最后是主程序:MainActivity.java

/*** 简易绘画程序*/
public class MainActivity extends AppCompatActivity {private DevinDrawPanle drawPanle;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);drawPanle = new DevinDrawPanle(this);setContentView(drawPanle);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.menu_main_option, menu);//添加子菜单SubMenu mnLineColor = menu.addSubMenu("颜色");SubMenu mnLineStyle = menu.addSubMenu("线型");SubMenu mnShapeStyle = menu.addSubMenu("形状");//实例化子菜单getMenuInflater().inflate(R.menu.menu_paint_colors, mnLineColor);getMenuInflater().inflate(R.menu.menu_line_style, mnLineStyle);getMenuInflater().inflate(R.menu.menu_shape_style, mnShapeStyle);return super.onCreateOptionsMenu(menu);}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.menu_item_save:saveFile();break;//颜色选择case R.id.color_red:drawPanle.setPaintColor(Color.RED);break;case R.id.color_orange:drawPanle.setPaintColor("#FF6600");//有重载形式break;case R.id.color_yellow:drawPanle.setPaintColor(Color.YELLOW);break;case R.id.color_green:drawPanle.setPaintColor(Color.GREEN);break;case R.id.color_cyan:drawPanle.setPaintColor("#0055CC");break;case R.id.color_blue:drawPanle.setPaintColor(Color.BLUE);break;case R.id.color_purple:drawPanle.setPaintColor("#8800CC");break;case R.id.color_black:drawPanle.setPaintColor(Color.BLACK);break;case R.id.color_gray:drawPanle.setPaintColor(Color.GRAY);break;//设置线条粗细case R.id.drawline1:drawPanle.setPaintStrokeWidth(1);break;case R.id.drawline2:drawPanle.setPaintStrokeWidth(2);break;case R.id.drawline3:drawPanle.setPaintStrokeWidth(3);break;case R.id.drawline4:drawPanle.setPaintStrokeWidth(4);break;case R.id.drawline5:drawPanle.setPaintStrokeWidth(5);break;//设置形状case R.id.drawbezier:drawPanle.setShapeStyle(0);break;case R.id.drawline:drawPanle.setShapeStyle(1);break;case R.id.drawrect:drawPanle.setShapeStyle(2);break;case R.id.drawcircle:drawPanle.setShapeStyle(3);break;case R.id.drawoval:drawPanle.setShapeStyle(4);break;case R.id.drawroundrect:drawPanle.setShapeStyle(5);break;default:break;}return super.onOptionsItemSelected(item);}/*** 保存文件*/private void saveFile() {File saveFile = new File(Environment.getExternalStorageDirectory(), "mybmp2.png");drawPanle.saveBitmap(saveFile);Toast.makeText(this, "文件保存在:" + saveFile.getAbsolutePath(), Toast.LENGTH_SHORT).show();}
}

运行效果:

简易的Android绘图程序相关推荐

  1. Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析

    ndroid应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析 在WindowManagerService服务这一侧,每一个应用程序窗口,即每一个Activity组件,都有一个 ...

  2. Android应用程序窗口(Activity)的测量(Measure)、布局(Layout)和绘制(Draw)过程分析(上)...

    在前面一篇文章中,我们分析了Android应用程序窗口的绘图表面的创建过程.Android应用程序窗口的绘图表面在创建完成之后,我们就可以从上到下地绘制它里面的各个视图了,即各个UI元素了.不过在绘制 ...

  3. Android绘图机制与处理技巧-更新中

    概述 这里我们主要来探讨下 Android屏幕的相关只是 Android绘图技巧 Android图像处理技巧 SurfaceView的使用 绘图技巧中,医生讲的比较粗略,更多的细节参考了 Keegan ...

  4. 《OpenGL ES 3.x游戏开发(上卷)》一1.5 Android应用程序运行的机制

    本节书摘来异步社区<OpenGL ES 3.x游戏开发(上卷)>一书中的第1章,第1.5节,作者: 吴亚峰 责编: 张涛,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  5. Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/7932268 在前面一篇文章中,我们分析了And ...

  6. Android应用程序请求SurfaceFlinger服务创建Surface的过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/7884628 前面我们已经学习过Android应 ...

  7. Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析

    在前两文中,我们分析了Activity组件的窗口对象和视图对象的创建过程.Activity组件在其窗口对象和视图对象创建完成之后,就会请求与WindowManagerService建立一个连接,即请求 ...

  8. Android从程序员到架构师之路3

    本文学习自高焕堂老师的Android从程序员到架构师之路系列教学视频 40 - 认识线程(Thread)模式a 1. 线程(Thread)概念 所谓线程(Thread) 是指一串连续的执行动作,以达成 ...

  9. 使用jQuery Mobile和Phone Gap开发Android应用程序

    使用jQuery Mobile和Phone Gap开发Android应用程序 1. 软件准备  要进行android app的开发,当然需要准备Java, eclipse和安装Android SDK, ...

最新文章

  1. 13、JsonResponse响应介绍
  2. 重新定义“物联网” GreenPeak助力合作伙伴构建智能家居
  3. 奥运会志愿者需要做哪些工作?
  4. [原创]基于Extjs的开源控件库 - http://extaspnet.codeplex.com/
  5. 1在mysql进行定义操作系统_Mysql基础知识一
  6. mysql的水平分表和垂直分表的区别
  7. DZY的根(思维水)
  8. 计算机管理的服务打不开,Windows 系统服务无法打开解决方法+操作命令详解
  9. hdu1197(十进制十六进制十二进制位数和)
  10. 如何把一个字符串的大小写取反(大写变小写, 小写变大写)
  11. linux下tomcat查看端口
  12. 数模(2)——多属性决策模型
  13. compare和compareTo方法的区别
  14. 华为 ACL与DHCP配置
  15. java 上下键_用键盘的上下左右键控制JAVA SWING UI中的组件的移动等事件 | 学步园...
  16. sql server,mysql,oracle的区别
  17. “人生路,处处风雨阻,莫畏难,有志事事成”。
  18. C语言程序运行的步骤
  19. 备忘录吕吕没有备忘录十新建_去弦备忘单
  20. 年度大型攻防实战全景:红蓝深度思考及多方联合推演

热门文章

  1. JTable获取选中行
  2. 关于CAN总线的理解
  3. javascript中caller和callee区别以及使用场景
  4. vscode 更换文件图标主题
  5. ubuntu安装VIM教程与命令详解
  6. SOHO帮客户找新品,如何拿到最优价?要不要选择大型机械类产品?
  7. 微信商户限制限3000 更变后 appid和openid不匹配
  8. opencv fisheye calibration(鱼眼相机校正)
  9. Android 新老两代 Camera API 大起底
  10. c#学Java - Java基本语法