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

例如以下图,有一个供用户自己随意绘图、涂鸦的app。

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

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

用户画完图之后能够保存图像。图像的文件名称是当前的时间。保存的位置是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" >
<item
android:id="@+id/menu1_sub1"
android:title="@string/menu1_sub1"/>
<item
android:id="@+id/menu1_sub2"
android:title="@string/menu1_sub2"/>
<item
android:id="@+id/menu1_sub3"
android:title="@string/menu1_sub3"/>
<item
android:id="@+id/menu1_sub4"
android:title="@string/menu1_sub4"/>
</group>
</menu>
</item>
<item
android:id="@+id/menu2"
android:title="@string/menu2"/>
<item
android:id="@+id/menu3"
android:title="@string/menu3"/>
<item
android:id="@+id/menu4"
android:title="@string/menu4"/>
<item
android: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-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<!-- 须要在SD卡写入数据的权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android: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 } @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(cacheBitmap, 0, 0, paint);// 把cacheBitmap画到DrawView上
} @Override
public 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.DrawView
android: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 { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} @Override
public 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;
} // 处理菜单事件
@Override
public 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

最新文章

  1. c#下volatile关键字
  2. java报表工具FineReport的SQL编辑框的语法简介
  3. IOS下自定义click事件使用alert引发的血案
  4. ABAP 内表的行列转换-发货通知单-打印到Excel里-NEW-(以运单号为单位显示ALV然后保存输出)
  5. lvs+keepalived 负载均衡
  6. 贪心+模拟 Codeforces Round #288 (Div. 2) C. Anya and Ghosts
  7. controlling the variance of request response times and not just worrying about maximizing queries per second
  8. ZOJ3765 Lights Splay树
  9. html学习笔记之position
  10. MSSQL导入数据时,出现“无法截断表 因为表正由Foreign key引用”错误
  11. [Java] 类和接口的初始化步骤 - 继承方面
  12. Android常用动画alpha和rotate同时使用
  13. 关于html5调用手机相机(原创)
  14. Selenium WebDriver + python 自动化测试框架
  15. IDEA指定.class文件输出位置
  16. 原生js触碰到底部触发函数;
  17. shell中使用类似Python的参数处理
  18. nexus、maven私服仓库(一)
  19. 4、keepalived高可用nginx负载均衡
  20. Python 多进程应用示例

热门文章

  1. $(&quot;[lay-id=&#39;&quot;+this.id+&quot;&#39;]&quot;)
  2. CAD参数绘制椭圆(网页版)
  3. Dynamic Web Module版本对应tomcat版本
  4. 如何HTML标签和JS中设置CSS3 var变量
  5. 通过反编译小程序来学习前端:wxappUnpacker
  6. Hibernate-02
  7. 零基础入门学习Python(22)--函数:递归是神马
  8. C#上位机开发(一)—— 了解上位机
  9. 2017icpc 西安 XOR
  10. 《C语言程序设计(第四版)》阅读心得(二)