前言

  除了使用已有的图片之外,Android应用常常需要在运行时根据场景动态生成2D图片,比如手机游戏,这就需要借助于Android2D绘图的支持。本篇博客主要讲解一下Android下使用Canvas进行绘图的相关操作。最后将以一个简单的Demo演示如何使用Canvas在ImageView上画图并保存。

  本篇博客的主要内容:

画布Canvas

  在Android下进行2D绘图需要Canvas类的支持,它位于"android.graphics.Canvas"包下,直译过来为画布的意思,用于完成在View上的绘图。    

  Canvas为提供了两个构造函数:

  • Canvas():创建一个空的Canvas对象。
  • Canvas(Bitmap bitmap):创建一个以bitmap位图为背景的Canvas。

  既然Canvas主要用于2D绘图,那么它也提供了很多相应的drawXxx()方法,方便我们在Canvas对象上画画,drawXxx()具有多种类型,可以画出:点、线、矩形、圆形、椭圆、文字、位图等的图形,这里就不再一一介绍了,只介绍几个Canvas中常用的方法:

  • void drawBitmap(Bitmap bitmap,float left,float top,Paint paint):在指定坐标绘制位图。
  • void drawLine(float startX,float startY,float stopX,float stopY,Paint paint):根据给定的起始点和结束点之间绘制连线。
  • void drawPath(Path path,Paint paint):根据给定的path,绘制连线。
  • void drawPoint(float x,float y,Paint paint):根据给定的坐标,绘制点。
  • void drawText(String text,int start,int end,Paint paint):根据给定的坐标,绘制文字。
  • int getHeight():得到Canvas的高度。
  • int getWidth():得到Canvas的宽度。

  

画笔Paint

  从上面列举的几个Canvas.drawXxx()的方法看到,其中都有一个类型为paint的参数,可以把它理解为一个"画笔",通过这个画笔,在Canvas这张画布上作画。 它位于"android.graphics.Paint"包下,主要用于设置绘图风格,包括画笔颜色、画笔粗细、填充风格等。

  Paint中提供了大量设置绘图风格的方法,这里仅列出一些常用的,高级的内容有时间再详细讲解:

  • 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):设置绘制文本时的文字大小。

使用Canvas绘图Demo

  既然已经简单讲解了Android下2D绘图的两个重要类,Canvas和Paint,那么下面通过一个简单的Demo来演示一下,这样加深大家的理解。

  在这个Demo中,将实现一个画图板的功能,当用户在触摸屏上移动的时候,即可在屏幕上绘制任意的图形。实现手绘功能其实是一种假象:表面上看起来可以随着用户在触摸屏上自由地画线,实际上依然利用的是Canvas的drawLine()方法画直线,每条直线都是从上一个移动事件发生点画到本次移动事件的的发生点。当用户在触摸屏上连续移动的时候,每次移动点之间的距离很小,多此极短的连线,肉眼看起来就是一个依照手指触摸移动的轨迹的连线。

  需要指出的是,如果程序每次都只是从上次移动事件的发生点绘制一条直线到本次拖动事件的发生点,那么当用户手指一旦离开触摸屏,再次引发触摸移动事件的时候,会导致前面绘制的内容被丢失。为了保留用户之前绘制的内容,程序需要借助于一个"双缓冲"的机制。

  之前讲解SurfaceView的时候,有讲到SurfaceView会自己维护一个双缓冲的缓冲区,但是在这里使用ImageView来展示绘图效果,它需要我们去维护双缓冲的机制。当用户在ImageView上进行"绘制"的时候,程序并不直接"绘制"到该ImageView组件上,而是先绘制到一个内存中的Bitmap对象(缓冲)上,等到内存中的Bitmap绘制好之后,再一次性的将Bitmap对象"绘制"到ImageView上。

  在这个Demo中,会监听ImageView的View.OnTouchListener事件的发生,它主要用户监听在View上的触摸事件。其中需要重写onTouch()方法,当用户触摸View的时候会调用这个方法,以下是它的完整签名:

    boolean onTouch(View v,MotionEvent event)

  它的返回值用于指定是否连续捕获触摸事件,而在它的参数中,View为当前引发触摸事件的View,而MotionEvent是当前引发触摸事件一些属性,这个类中定义了一系列的静态常量,用于表示当前触摸的动作,比如:

  • MotionEvent.ACTION_DOWN:手指触摸屏幕。
  • MotionEvent.ACTION_MOVE:手指移动。
  • MotionEvent.ACTION_UP:手指离开屏幕。

  

  Demo中的主要内容已经讲解清楚,下面直接上代码了,代码中注释比较详细,就不再赘述了。

  布局代码:activity_main.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" > <Button
android:id="@+id/btn_resume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重新画图" /> <Button
android:id="@+id/btn_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存图片" />
</LinearLayout> <ImageView
android:id="@+id/iv_canvas"
android:layout_width="match_parent"
android:layout_height="match_parent" /> </LinearLayout>

activity_main.xml

  实现代码:MainActivity.java

 package cn.bgtx.canvasdemo;

 import java.io.File;
import java.io.FileOutputStream;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast; public class MainActivity extends Activity {
private Button btn_save, btn_resume;
private ImageView iv_canvas;
private Bitmap baseBitmap;
private Canvas canvas;
private Paint paint; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 初始化一个画笔,笔触宽度为5,颜色为红色
paint = new Paint();
paint.setStrokeWidth(5);
paint.setColor(Color.RED); iv_canvas = (ImageView) findViewById(R.id.iv_canvas);
btn_save = (Button) findViewById(R.id.btn_save);
btn_resume = (Button) findViewById(R.id.btn_resume); btn_save.setOnClickListener(click);
btn_resume.setOnClickListener(click);
iv_canvas.setOnTouchListener(touch);
} private View.OnTouchListener touch = new OnTouchListener() {
// 定义手指开始触摸的坐标
float startX;
float startY; @Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
// 用户按下动作
case MotionEvent.ACTION_DOWN:
// 第一次绘图初始化内存图片,指定背景为白色
if (baseBitmap == null) {
baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(),
iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(baseBitmap);
canvas.drawColor(Color.WHITE);
}
// 记录开始触摸的点的坐标
startX = event.getX();
startY = event.getY();
break;
// 用户手指在屏幕上移动的动作
case MotionEvent.ACTION_MOVE:
// 记录移动位置的点的坐标
float stopX = event.getX();
float stopY = event.getY(); //根据两点坐标,绘制连线
canvas.drawLine(startX, startY, stopX, stopY, paint); // 更新开始点的位置
startX = event.getX();
startY = event.getY(); // 把图片展示到ImageView中
iv_canvas.setImageBitmap(baseBitmap);
break;
case MotionEvent.ACTION_UP: break;
default:
break;
}
return true;
}
};
private View.OnClickListener click = new OnClickListener() { @Override
public void onClick(View v) { switch (v.getId()) {
case R.id.btn_save:
saveBitmap();
break;
case R.id.btn_resume:
resumeCanvas();
break;
default:
break;
}
}
}; /**
* 保存图片到SD卡上
*/
protected void saveBitmap() {
try {
// 保存图片到SD卡上
File file = new File(Environment.getExternalStorageDirectory(),
System.currentTimeMillis() + ".png");
FileOutputStream stream = new FileOutputStream(file);
baseBitmap.compress(CompressFormat.PNG, 100, stream);
Toast.makeText(MainActivity.this, "保存图片成功", 0).show(); // Android设备Gallery应用只会在启动的时候扫描系统文件夹
// 这里模拟一个媒体装载的广播,用于使保存的图片可以在Gallery中查看
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
intent.setData(Uri.fromFile(Environment
.getExternalStorageDirectory()));
sendBroadcast(intent);
} catch (Exception e) {
Toast.makeText(MainActivity.this, "保存图片失败", 0).show();
e.printStackTrace();
}
} /**
* 清除画板
*/
protected void resumeCanvas() {
// 手动清除画板的绘图,重新创建一个画板
if (baseBitmap != null) {
baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(),
iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(baseBitmap);
canvas.drawColor(Color.WHITE);
iv_canvas.setImageBitmap(baseBitmap);
Toast.makeText(MainActivity.this, "清除画板成功,可以重新开始绘图", 0).show();
}
}
}

  效果展示(随手涂鸦了个笑脸,发现还是蛮有喜感的,大家将就着看看吧):

 
  源码下载

Android--使用Canvas绘图的更多相关文章

  1. Android中Canvas绘图基础详解(附源码下载) (转)

    Android中Canvas绘图基础详解(附源码下载) 原文链接  http://blog.csdn.net/iispring/article/details/49770651   AndroidCa ...

  2. 【转】Android Canvas绘图详解(图文)

    转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android Canvas绘图详解(图文) 泡 ...

  3. Android Canvas绘图详解(图文)

    编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! Andr ...

  4. 【转】Android Canvas绘图详解

    转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android中使用图形处理引擎,2D部分是an ...

  5. HTML5 学习总结(四)——canvas绘图、WebGL、SVG

    一.Canvas canvas是HTML5中新增一个HTML5标签与操作canvas的javascript API,它可以实现在网页中完成动态的2D与3D图像技术.<canvas> 标记和 ...

  6. canvas绘图、WebGL、SVG

    目录 一.Canvas 1.1.创建canvas元素 1.2.画线 1.3.绘制矩形 1.4.绘制圆弧 1.5.绘制图像 1.6.绘制文字 1.7.随机颜色与简单动画 二.WebGL 2.1.HTML ...

  7. HTML5 学习笔记(四)——canvas绘图、WebGL、SVG

    一.Canvas canvas是HTML5中新增一个HTML5标签与操作canvas的javascript API,它可以实现在网页中完成动态的2D与3D图像技术.<canvas> 标记和 ...

  8. Android之canvas详解

    首先说一下canvas类: Class Overview The Canvas class holds the "draw" calls. To draw something, y ...

  9. Android自定义View绘图实现拖影动画

    前几天在"Android绘图之渐隐动画"一文中通过画线实现了渐隐动画,但里面有个问题,画笔较粗(大于1)时线段之间会有裂隙,我又改进了一下.这次效果好多了. 先看效果吧: 然后我们 ...

随机推荐

  1. linux ntp 服务器和用户端

    ntp 服务器 1.输入 rpm -qa|grep ntp 查看是否安装了ntp服务器 2.如果没安装 yum -y install ntp 安装 3.修改 /etc/ntp.conf 将原serve ...

  2. Yaf零基础学习总结1-Yaf框架简介

    从今天开始,给大家讲解下yaf框架,讲解之前肯定要了解下yaf是个什么东西,当然,从标题我们已经知道yaf是个PHP框架了,也许大家对于PHP框架并不陌生,一般PHP程序员用过的框架至少有一两个吧,国 ...

  3. C++混合编程之idlcpp教程Lua篇(2)

    在上一篇 C++混合编程之idlcpp教程(一) 中介绍了 idlcpp 工具的使用.现在对 idlcpp 所带的示例教程进行讲解,这里针对的 Lua 语言的例子.首先看第一个示例程序 LuaTuto ...

  4. 腾讯DBA官方博客开通了

    腾讯DBA官方博客开通了,欢迎交流哈..     http://tencentdba.com   腾讯互娱游戏DBA团队一直致力于为游戏提供稳定.高效的DB运营服务,这是我们团队的使命. 过去DBA团 ...

  5. IHttpActionResult – new way of creating responses in ASP.NET Web API 2

    先收藏这篇文章. http://www.strathweb.com/2013/06/ihttpactionresult-new-way-of-creating-responses-in-asp-net ...

  6. Dynamic CRM 2013学习笔记(二十四)页面保存前进行逻辑验证

    我们有时要验证下页面上的一些逻辑,比如开始时间不能晚于结束时间,不对时不让保存.我们可以在相关的字段事件上处理,但这如果要判断的字段比较多时,就比较麻烦了. 这时候我们就可以利用Form的OnSave ...

  7. shellKali Linux Web 渗透测试— 初级教程(第三课)

    shellKali Linux Web 渗透测试— 初级教程(第三课) 文/玄魂 目录 shellKali Linux Web 渗透测试—初级教程(第三课) 课程目录 通过google hack寻找测 ...

  8. 高手速成android开源项目【blog篇】

    主要介绍那些乐于分享并且有一些很不错的开源项目的个人和组织.Follow大神,深挖大神的项目和following,你会发现很多. 一.个人 JakeWharton 就职于SquareGithub地址: ...

  9. 腾讯云CentOS 6.6安装 Nginx

    一.下载Nginx 从Nginx的官网(http://nginx.org/en/download.html)下载Nginx的最新版本,这里我下载的是nginx-1.9.12. 下载完成后,得到一个如下 ...

  10. 除非 Windows Activation Service (WAS)和万维网发布服务(W3SVC)均处于运行状态,否则无法启动网站。目前,这两项服务均处于停止状态。

    win7 IIS 所有网站都停止了,启动提示: 除非 Windows Activation Service (WAS)和万维网发布服务(W3SVC)均处于运行状态,否则无法启动网站.目前,这两项服务均 ...