利用一个简单的画图app来说明安卓的图形处理类与自定义View的应用。

如下图,有一个供用户自己任意画图、涂鸦的app,

这里不做那么花俏了,仅提供黑白两色,但可以改变笔尖的粗细。

实质上这里的橡皮擦就是白色的画笔,根本不用使用到画笔的setXfermode方法,要搞一堆复杂的工程。

用户画完图之后可以保存图像。图像的文件名是当前的时间,保存的位置是sdcard的根目录。

制作过程如下:

1、先设置好字体文件res\values\strings.xml,主要是app的名称与菜单各个子项的字符。

<?xml version="1.0" encoding="utf-8"?>
<resources><string name="app_name">画图</string><string name="menu1">画笔宽度</string><string name="menu1_sub1">1</string><string name="menu1_sub2">5</string><string name="menu1_sub3">10</string><string name="menu1_sub4">50</string><string name="menu2">画笔</string><string name="menu3">橡皮擦</string><string name="menu4">保存</string><string name="menu5">退出</string><string name="menu_author">作者:yongh701</string></resources>

2、之后就是菜单文件的设置,这里不再赘述了,在《【Android】日期拾取器、时间拾取器与菜单》( 点击打开链接)与《【Android】app透明与字体颜色更变、上下文菜单》( 点击打开链接)两篇文章都详细搞过菜单的东西,主要是第一个菜单选项“画笔宽度”是带有子项的,因此,设置菜单的id是分别给子项设置id,而不是主项,主项无须id。

<menu xmlns:android="http://schemas.android.com/apk/res/android" ><item android:title="@string/menu1"><menu><group android:checkableBehavior="single" ><itemandroid:id="@+id/menu1_sub1"android:title="@string/menu1_sub1"/><itemandroid:id="@+id/menu1_sub2"android:title="@string/menu1_sub2"/><itemandroid:id="@+id/menu1_sub3"android:title="@string/menu1_sub3"/><itemandroid:id="@+id/menu1_sub4"android:title="@string/menu1_sub4"/></group></menu></item><itemandroid:id="@+id/menu2"android:title="@string/menu2"/><itemandroid:id="@+id/menu3"android:title="@string/menu3"/><itemandroid:id="@+id/menu4"android:title="@string/menu4"/><itemandroid:id="@+id/menu5"android:title="@string/menu5"/><item android:title="@string/menu_author"/></menu>

3、由于一会儿还要把用户画出来的图片写入的sdcard卡,因此将在AndroidManifest.xml申请sdcard的写入的权限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.painter"android:versionCode="1"android:versionName="1.0" ><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="18" /><!-- 需要在SD卡写入数据的权限 --><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activityandroid:name="com.painter.MainActivity"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

4、之后就像《【Android】自定义View、画布Canvas与画笔Paint》( 点击打开链接)一样,新建一个自定义的View,这里是DrawView。这个DrawView是本app实现的核心。其构造方法,使用public DrawView(Context context, AttributeSet attrs) {super(context, attrs);} ,这个带有两个参数的构造方法,因为一会儿这个DrawView将以xml的方式直接布置在MainActivity。同时通过Alt+Shift+S->V选择继承protected void onDraw(Canvas canvas) {},public boolean onTouchEvent(MotionEvent event) {}这两个方法,一个是安卓图像处理技术的基本方法onDraw,一个是用户触摸这个View时发生的事件onTouchEvent方法。同时自己添加一个saveBitmap方法,用来实现图片的最终的保存。

DrawView.java的代码如下:

package com.painter;import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Environment;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;public class DrawView extends View {private Bitmap cacheBitmap;// 画纸private Canvas cacheCanvas;// 创建画布、画家private Path path;// 绘图的路径public Paint paint;// 画笔private float preX, preY;// 之前的XY的位置,用于下面的手势移动private int view_width, view_height;// 屏幕的高度与宽度public DrawView(Context context, AttributeSet attrs) {super(context, attrs);path = new Path();paint = new Paint();cacheCanvas = new Canvas();// 获取屏幕的高度与宽度view_width = context.getResources().getDisplayMetrics().widthPixels;view_height = context.getResources().getDisplayMetrics().heightPixels;cacheBitmap = Bitmap.createBitmap(view_width, view_height,Config.ARGB_8888);// 建立图像缓冲区用来保存图像cacheCanvas.setBitmap(cacheBitmap);cacheCanvas.drawColor(Color.WHITE);paint.setColor(Color.BLACK);// 设置画笔的默认颜色paint.setStyle(Paint.Style.STROKE);// 设置画笔的填充方式为无填充、仅仅是画线paint.setStrokeWidth(1);// 设置画笔的宽度为1}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawBitmap(cacheBitmap, 0, 0, paint);// 把cacheBitmap画到DrawView上}@Overridepublic boolean onTouchEvent(MotionEvent event) {// 获取触摸位置float x = event.getX();float y = event.getY();switch (event.getAction()) {// 获取触摸的各个瞬间case MotionEvent.ACTION_DOWN:// 手势按下path.moveTo(x, y);// 绘图的起始点preX = x;preY = 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, paint);// 绘制路径}break;case MotionEvent.ACTION_UP:path.reset();break;}invalidate();return true;}public void saveBitmap() throws Exception {String sdpath = Environment.getExternalStorageDirectory().getAbsolutePath();// 获取sdcard的根路径String filename = new SimpleDateFormat("yyyyMMddhhmmss",Locale.getDefault()).format(new Date(System.currentTimeMillis()));// 产生时间戳,称为文件名File file = new File(sdpath + File.separator + filename + ".png");file.createNewFile();FileOutputStream fileOutputStream = new FileOutputStream(file);cacheBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);// 以100%的品质创建png// 人走带门fileOutputStream.flush();fileOutputStream.close();Toast.makeText(getContext(),"图像已保存到" + sdpath + File.separator + filename + ".png",Toast.LENGTH_SHORT).show();}}

整个DrawView.java做了如下的事情:

(1)设置一张画纸cacheBitmap,两个画家Canvas与cacheCanvas,一支画笔paint,这里之所以要有两位画家,是因为onDraw方法独占Canvas这个类成员。用户每触摸一次屏幕,都会触发onTouchEvent方法,设置cacheCanvas把用户触摸时绘制的路径放到画纸cacheBitmap上,通过invalidate();方法的调用再次onDraw方法,Canvas把画纸cacheBitmap放到DrawView这个我们自定义的View上。用户每触摸一次屏幕都会执行一次这个操作。

(2)由于每次执行invalidate()方法,都会触发onDraw方法,因此初始化的工作应通通放在自定义View的构造方法中以节省内存,这个问题在《【Android】利用自定义View的重绘实现拖动移动,获取组件的尺寸》(点击打开链接)已经讲过了,这里不再赘述。构造方法,完成画家(画布)Canvas与画笔Paint,绘图路径Path的初始化。

关键是要把初始化之后的画纸cacheBitmap放到画家cacheCanvas手上,同时命令画家cacheCanvas把这张画纸cacheBitmap全部涂白,也就是说把画图的背景颜色设置为白色。否则一会儿你保存出来的图像的背景色默认是黑色的。虽然你看到的自定义View是白色的。

在画笔Paint初始化的事情,注意要把画笔设置为paint.setStyle(Paint.Style.STROKE);仅仅是画边的方法,这样才能做到涂鸦的效果,否则画笔默认是,附件画图画矩形那种拖泥带水的效果。

(3)触摸事件onTouchEvent里的作图方法这里反而没什么好说的,计算机图形学中最基本的内容。不懂就照复制就是了。

(4)最后的保存图像的方法saveBitmap()也没什么好说的。就是安卓对sdcard卡的操作,具体见《【Android】读取sdcard上的图片》(点击打开链接),与Java对文件的操作的综合,具体见《【Java】输入与输出与JDK1.5之后的新型字符串StringBuilder》(点击打开链接)。

5、通过自定义的View,能让res\layout\activity_main.xml这个MainActivity的布局xml,与MainActivity.java的代码变得简洁。res\layout\activity_main.xml将变得如下的简短,就放一个DrawView,该实现的东西都在这个自定义View中完成。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" ><com.painter.DrawViewandroid:id="@+id/drawView1"android:layout_width="match_parent"android:layout_height="match_parent" /></FrameLayout>

6、最后在MainActivity.java中实现指明各个菜单的实现方法就能完成整个app,OnCreate方法根本就是什么都没有,仅仅是加载布局文件。

package com.painter;import android.os.Bundle;
import android.app.Activity;
import android.graphics.Color;
import android.view.Menu;
import android.view.MenuItem;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}// 处理菜单事件@Overridepublic boolean onOptionsItemSelected(MenuItem item) {DrawView drawView = (DrawView) findViewById(R.id.drawView1);switch (item.getItemId()) {// 设置id为menu_exit的菜单子项所要执行的方法。case R.id.menu1_sub1:drawView.paint.setStrokeWidth(1);break;case R.id.menu1_sub2:drawView.paint.setStrokeWidth(5);break;case R.id.menu1_sub3:drawView.paint.setStrokeWidth(10);break;case R.id.menu1_sub4:drawView.paint.setStrokeWidth(50);break;case R.id.menu2:drawView.paint.setColor(Color.BLACK);break;case R.id.menu3:drawView.paint.setColor(Color.WHITE);break;case R.id.menu4:try {drawView.saveBitmap();} catch (Exception e) {e.printStackTrace();}break;case R.id.menu5:System.exit(0);// 结束程序break;}return true;}}

最后,我上传了一份源码给大家:http://download.csdn.net/detail/yongh701/8900457

【Android】自定义View、画家(画布)Canvas与画笔Paint的应用——画图、涂鸦板app的实现相关推荐

  1. 【Android】自定义View、画布Canvas与画笔Paint

    安卓自定义View其实很简单.这个View可以像<[Android]利用Java代码布局,按钮添加点击事件>(点击打开链接)一样,利用Java代码生成一系列的组件.也可以配合画布Canva ...

  2. Android 自定义View 实例2_Clipping Canvas

    上一篇 Android 自定义View 实例_ 画图  参考: https://blog.csdn.net/whjk20/article/details/115639448 这里是Canvas 的裁剪 ...

  3. android画布设置最外层,Android自定义View高级(三)-Canvas之画布操作

    一.Canvas简介 Canvas我们可以称之为画布,能够在上面绘制各种东西,是Android平台2D图形绘制的基础. 二.Canvas的常用操作 操作类型 相关API 备注 绘制颜色 drawCol ...

  4. android自定义起止时间的时间刻度尺,Android 自定义View篇(六)实现时钟表盘效果...

    前言 Android 自定义 View 是高级进阶不可或缺的内容,日常工作中,经常会遇到产品.UI 设计出花里胡哨的界面.当系统自带的控件不能满足开发需求时,就只能自己动手撸一个效果. 本文就带自定义 ...

  5. Android 自定义View —— Canvas

    上一篇在android 自定义view Paint 里面 说了几种常见的Point 属性 绘制图形的时候下面总有一个canvas ,Canvas 是是画布 上面可以绘制点,线,正方形,圆,等等,需要和 ...

  6. android代码实现手机加速功能,Android自定义View实现内存清理加速球效果

    Android自定义View实现内存清理加速球效果 发布时间:2020-09-21 22:21:57 来源:脚本之家 阅读:105 作者:程序员的自我反思 前言 用过猎豹清理大师或者相类似的安全软件, ...

  7. android 自定义view 动画效果,Android自定义view----音乐播放动画

    先给大家看一下效果,因为我也不知道这个东西具体叫什么,标题上面写的是"音乐播放动画",可能描述的不太准确. 效果图.gif 前言 最近项目中做了一个音频播放的功能,播放条上需要一个 ...

  8. android 立体 流量球,Android自定义View——实现水波纹效果类似剩余流量球

    Android自定义View--实现水波纹效果类似剩余流量球 三个点   pre   ber   block   span   初始化   move   理解最近突然手痒就想搞个贝塞尔曲线做个水波纹效 ...

  9. android 自定义取色器,【Android自定义View】仿Photoshop取色器ColorPicker(二)

    ColorPicker 一款仿Photoshop取色器的Android版取色器. 前言 上一篇已经简单介绍了ColorPicker的项目结构以及两种颜色空间,接下来我们详细解析一下ColorPicke ...

最新文章

  1. torch.nn.functional.cross_entropy.ignore_index
  2. java 创建 HMAC 签名
  3. java wordcount程序_WordCount程序(java)
  4. MySQL 存储过程的基本用法
  5. 硬件密码组件的硬件结构、作用及实现应用设计
  6. 设置View单个圆角
  7. 熟悉linux运行环境,实验一 熟悉Ubuntu环境
  8. 德媒看2019年的世界:西方在争吵,中国在崛起,非洲在增长……
  9. c语言中typeof关键字
  10. OS开发UI篇—Quartz2D使用(截屏)
  11. swoole 服务端heartbeat_check_interval心跳检测 客户端 swoole_timer_tick 发送心跳包 这里只是个demo参考
  12. 如何减少java里的分支_idea切换分支的时候,忽略一些无用的修改设置
  13. codesys编程_明晚20:00,CODESYS教您制作可编程控制器
  14. 苍穹影视V20七彩视界免授权开源源码
  15. drawRGB在N7370上的问题
  16. MP3编码之shine压缩异常解决
  17. 《RISC-V架构与嵌入式开发快速入门_胡振波著》学习笔记
  18. SpringBoot防止大量请求攻击
  19. Unity3d:UGUI,UI与特效粒子层级,2018.2以上版本BakeMesh,粒子在两个Image之间且在ScrollView
  20. python学习-day18、文件处理、

热门文章

  1. 微信小程序 自定义组件中 triggerEvent() 函数失效的一种情况 (Cannot read property ‘triggerEvent‘ of undefined)
  2. 教你用matlab做损伤分析,基于缩尺斜拉桥模型的结构损伤分析试验研究
  3. uni-app 对FastAdmin微信公众号授权登录实例
  4. 教你制作Python进度条
  5. 苹果Mac系统中优化工具MacBooster 7有哪些问题?
  6. 购买韩国服务器时有哪些方面需要注意?
  7. STM8S903K3T6C基于IAR开发GPIO点灯示例
  8. matlab伽玛分布如何表示,matlab gamma函数
  9. 模拟量信号干扰的主要原因及解决方案
  10. JS进阶webAPIs