热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android编程实现手绘及保存为图片的方法(附demo源码下载)

这篇文章主要介绍了Android编程实现手绘及保存为图片的方法,涉及Android画布的使用及图片的操作技巧,并附带了demo源码供读者下载,需要的朋友可以参考下

本文实例讲述了Android编程实现手绘及保存为图片的方法。分享给大家供大家参考,具体如下:

运行效果图预览:

应 yzuo_08 要求做了此Demo,跟以前那个手写板Demo不同的是可以将画布的内容保存为图片。

附上关键代码:

MainView.java

package com.tszy.views; 
import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.Bitmap.CompressFormat; 
import android.graphics.Bitmap.Config; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.Path; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.View; 
public class MainView extends View { 
 private Paint paint; 
 private Canvas cacheCanvas; 
 private Bitmap cachebBitmap; 
 private Path path; 
 private int clr_bg, clr_fg; 
 public MainView(Context context, AttributeSet attrs) { 
  super(context, attrs); 
  clr_bg = Color.WHITE; 
  clr_fg = Color.CYAN; 
  paint = new Paint(); 
  paint.setAntiAlias(true); // 抗锯齿 
  paint.setStrokeWidth(3); // 线条宽度 
  paint.setStyle(Paint.Style.STROKE); // 画轮廓 
  paint.setColor(clr_fg); // 颜色 
  path = new Path(); 
  // 创建一张屏幕大小的位图,作为缓冲 
  cachebBitmap = Bitmap.createBitmap(480, 800, Config.ARGB_8888); 
  cacheCanvas = new Canvas(cachebBitmap); 
  cacheCanvas.drawColor(clr_bg); 
 } 
 public MainView(Context context) { 
  super(context); 
 } 
 @Override 
 protected void onDraw(Canvas canvas) { 
  canvas.drawColor(clr_bg); 
  // 绘制上一次的,否则不连贯 
  canvas.drawBitmap(cachebBitmap, 0, 0, null); 
  canvas.drawPath(path, paint);
 } 
 /** 
  * 清空画布 
  */ 
 public void clear() { 
  path.reset(); 
  cacheCanvas.drawColor(clr_bg); 
  invalidate(); 
 } 
 /** 
  * 将画布的内容保存到文件 
  * @param filename 
  * @throws FileNotFoundException 
  */ 
 public void saveToFile(String filename) throws FileNotFoundException { 
  File f = new File(filename); 
  if(f.exists()) 
   throw new RuntimeException("文件:" + filename + " 已存在!"); 
  FileOutputStream fos = new FileOutputStream(new File(filename)); 
  //将 bitmap 压缩成其他格式的图片数据 
  cachebBitmap.compress(CompressFormat.PNG, 50, fos); 
  try { 
   fos.close(); 
  } catch (IOException e) { 
   // TODO Auto-generated catch block 
   e.printStackTrace(); 
  } 
 } 
 private float cur_x, cur_y; 
 private boolean isMoving; 
 @Override 
 public boolean onTouchEvent(MotionEvent event) { 
  // TODO Auto-generated method stub 
  float x = event.getX(); 
  float y = event.getY(); 
  switch (event.getAction()) { 
   case MotionEvent.ACTION_DOWN : { 
    cur_x = x; 
    cur_y = y; 
    path.moveTo(cur_x, cur_y); 
    isMoving = true; 
    break; 
   } 
   case MotionEvent.ACTION_MOVE : { 
    if (!isMoving) 
     break; 
    // 二次曲线方式绘制 
    path.quadTo(cur_x, cur_y, x, y); 
    // 下面这个方法貌似跟上面一样 
    // path.lineTo(x, y); 
    cur_x = x; 
    cur_y = y; 
    break; 
   } 
   case MotionEvent.ACTION_UP : { 
    // 鼠标弹起保存最后状态 
    cacheCanvas.drawPath(path, paint); 
    path.reset(); 
    isMoving = false; 
    break; 
   } 
  } 
  // 通知刷新界面 
  invalidate(); 
  return true; 
 } 
}

Activity 代码:

@Override 
public void onClick(View v) { 
  // TODO Auto-generated method stub 
  switch (v.getId()) { 
   case R.id.iv_btn_clear : 
    view.clear(); 
    break; 
   case R.id.iv_btn_save : { 
    try { 
     String sdState = Environment.getExternalStorageState(); // 判断sd卡是否存在 
     // 检查SD卡是否可用 
     if (!sdState.equals(android.os.Environment.MEDIA_MOUNTED)) { 
      Toast.makeText(this, "SD卡未准备好!", Toast.LENGTH_SHORT).show(); 
      break; 
     } 
     //获取系统图片存储路径 
     File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); 
     // Make sure the Pictures directory exists. 
     path.mkdirs(); 
     //根据当前时间生成图片名称 
     Calendar c = Calendar.getInstance(); 
     String name = "" 
       + c.get(Calendar.YEAR) + c.get(Calendar.MONTH) + c.get(Calendar.DAY_OF_MONTH) 
       + c.get(Calendar.HOUR_OF_DAY) + c.get(Calendar.MINUTE) + c.get(Calendar.SECOND) 
        + ".png"; 
     //合成完整路径,注意 / 分隔符 
     String string = path.getPath() + "/" + name; 
     view.saveToFile(string); 
     Toast.makeText(this, "保存成功!\n文件保存在:" + string, Toast.LENGTH_LONG).show(); 
    } catch (FileNotFoundException e) { 
     Toast.makeText(this, "保存失败!\n" + e, Toast.LENGTH_LONG).show(); 
    } 
    break; 
   } 
  } 
}

没什么难度,主要是将Bitmap转PNG图片那里,找了一会发现 Canvas 没有直接或间接保存的方法,刚好这里我使用了双缓冲,另一块画布的内容位图自己创建的,很自然想到将这个画布的位图保存为文件即可。

再查看 Bitmap 有个 compress(CompressFormat format, int quality,OutputStream stream) 方法,很明显将文件输出流传给这个方法就OK

@Override 
public void onClick(View v) { 
  // TODO Auto-generated method stub 
  switch (v.getId()) { 
   case R.id.iv_btn_clear : 
    view.clear(); 
    break; 
   case R.id.iv_btn_save : { 
    try { 
     String sdState = Environment.getExternalStorageState(); // 判断sd卡是否存在 
     // 检查SD卡是否可用 
     if (!sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
      Toast.makeText(this, "SD卡未准备好!", Toast.LENGTH_SHORT).show(); 
      break; 
     } 
     //获取系统图片存储路径 
     File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); 
     // Make sure the Pictures directory exists. 
     path.mkdirs(); 
     //根据当前时间生成图片名称 
     Calendar c = Calendar.getInstance(); 
     String name = "" 
       + c.get(Calendar.YEAR) + c.get(Calendar.MONTH) + c.get(Calendar.DAY_OF_MONTH) 
       + c.get(Calendar.HOUR_OF_DAY) + c.get(Calendar.MINUTE) + c.get(Calendar.SECOND) 
        + ".png"; 
     //合成完整路径,注意 / 分隔符 
     String string = path.getPath() + "/" + name; 
     view.saveToFile(string); 
     Toast.makeText(this, "保存成功!\n文件保存在:" + string, Toast.LENGTH_LONG).show(); 
    } catch (FileNotFoundException e) { 
     Toast.makeText(this, "保存失败!\n" + e, Toast.LENGTH_LONG).show(); 
    } 
    break; 
   } 
  } 
}

完整实例代码点击此处本站下载。

希望本文所述对大家Android程序设计有所帮助。


推荐阅读
  • 在数字图像处理中,Photoshop 的直方图是一个重要的工具,它能够精确地反映图像中不同亮度级别的分布情况。通过分析直方图,用户可以深入了解图像的曝光、对比度和色调范围,从而进行更精细的调整。直方图不仅模拟了物体表面反射光线的原理,还能帮助摄影师和设计师更好地掌握图像的明暗细节,优化视觉效果。 ... [详细]
  • Android动态界面布局设计与实现
    在最近的项目中,我们集成了第三方Geesee的直播视频功能,遇到了一个动态界面布局的挑战。具体需求是在用户点击按钮时,能够实现视频视图与文档视图的位置互换。此外,还需要确保文档视图在不同屏幕尺寸下保持良好的显示效果。为了实现这一目标,我们采用了灵活的布局管理策略,并结合了自定义视图组件,以提升用户体验和界面的适应性。通过这种方式,不仅解决了动态布局的问题,还增强了应用的交互性和视觉效果。 ... [详细]
  • 推荐一款出色的移动应用原型设计工具——Tiggr(http://gotiggr.com)。该工具基于Flash技术开发,支持Web、iPhone和Android等多种平台的原型设计。虽然需要注册账号才能使用,但其强大的功能和易用性使其成为开发者和设计师的理想选择。 ... [详细]
  • 将解压缩版Tomcat集成至系统服务
    将解压缩版Tomcat集成至系统服务的方法如下:首先,在命令行中导航至Tomcat的`bin`目录,运行`service.bat install`命令以安装服务。需要注意的是,服务名称和显示名称已在`service.bat`脚本中预设,默认情况下会随不同版本有所变化。此外,建议检查并配置相关参数,确保服务能够稳定运行。 ... [详细]
  • 在Android开发中,BroadcastReceiver(广播接收器)是一个重要的组件,广泛应用于多种场景。本文将深入解析BroadcastReceiver的工作原理、应用场景及其具体实现方法,帮助开发者更好地理解和使用这一组件。通过实例分析,文章详细探讨了静态广播的注册方式、生命周期管理以及常见问题的解决策略,为开发者提供全面的技术指导。 ... [详细]
  • Java能否直接通过HTTP将字节流绕过HEAP写入SD卡? ... [详细]
  • 题目探讨了在无向图中求解点连通数的问题,具体涉及UVA1660和POJ1966两个经典问题。通过最小割算法的应用,分析了如何高效地确定网络中的关键节点和路径,为电缆电视网络的优化设计提供了理论支持。该研究不仅验证了最小割算法的有效性,还为进一步探索复杂网络的连通性和鲁棒性奠定了基础。 ... [详细]
  • 在 POJ1651 的乘法谜题挑战中,如果选手按相反顺序选择卡片,即先选 50,再选 20,最后选 1,则最终得分会有所不同。题目要求输入的第一行包含... 改写后的摘要:在 POJ1651 的乘法谜题挑战中,如果选手按照逆序选取卡片,例如依次选择 50、20 和 1,最终的得分将发生变化。题目首先要求输入的第一行包括... ... [详细]
  • 深入解析 Android 中 EditText 的 getLayoutParams 方法及其代码应用实例 ... [详细]
  • 资源管理器的基础架构包括三个核心组件:1)资源池,用于将CPU和内存等资源分配给不同的容器;2)负载组,负责承载任务并将其分配到相应的资源池;3)分类函数,用于将不同的会话映射到合适的负载组。该系统提供了两种主要的资源管理策略。 ... [详细]
  • CSS3 @font-face 字体应用技术解析与实践
    在Web前端开发中,HTML教程和CSS3的结合使得网页设计更加多样化。长期以来,Web设计师受限于“web-safe”字体的选择。然而,CSS3中的`@font-face`规则允许从服务器端加载自定义字体,极大地丰富了网页的视觉效果。通过这一技术,设计师可以自由选择和使用各种字体,提升用户体验和页面美观度。本文将深入解析`@font-face`的实现原理,并提供实际应用案例,帮助开发者更好地掌握这一强大工具。 ... [详细]
  • 在Android应用开发中,实现与MySQL数据库的连接是一项重要的技术任务。本文详细介绍了Android连接MySQL数据库的操作流程和技术要点。首先,Android平台提供了SQLiteOpenHelper类作为数据库辅助工具,用于创建或打开数据库。开发者可以通过继承并扩展该类,实现对数据库的初始化和版本管理。此外,文章还探讨了使用第三方库如Retrofit或Volley进行网络请求,以及如何通过JSON格式交换数据,确保与MySQL服务器的高效通信。 ... [详细]
  • AngularJS 进阶指南:第三部分深入解析
    在本文中,我们将深入探讨 AngularJS 的指令模型,特别是 `ng-model` 指令。`ng-model` 指令用于将 HTML 元素与应用程序数据进行双向绑定,支持多种数据类型验证,如数字、电子邮件地址和必填项检查。此外,我们还将介绍如何利用该指令优化表单验证和数据处理流程,提升开发效率和用户体验。 ... [详细]
  • 本文深入解析了Java面向对象编程的核心概念及其应用,重点探讨了面向对象的三大特性:封装、继承和多态。封装确保了数据的安全性和代码的可维护性;继承支持代码的重用和扩展;多态则增强了程序的灵活性和可扩展性。通过具体示例,文章详细阐述了这些特性在实际开发中的应用和优势。 ... [详细]
  • 提升Android开发效率:Clean Code的最佳实践与应用
    在Android开发中,提高代码质量和开发效率是至关重要的。本文介绍了如何通过Clean Code的最佳实践来优化Android应用的开发流程。以SQLite数据库操作为例,详细探讨了如何编写高效、可维护的SQL查询语句,并将其结果封装为Java对象。通过遵循这些最佳实践,开发者可以显著提升代码的可读性和可维护性,从而加快开发速度并减少错误。 ... [详细]
author-avatar
永无止境
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有