技术开发 频道

教你在谷歌Android平台中处理图片

IT168 专稿】Android是谷歌推出的业界首个完全开放且免费的移动应用开发平台,自从去年11月份谷歌宣布举办总奖金为1000万美元的Android开发者大赛以来,人们对了解它、使用它的热情逐渐升温。自开赛以来全世界已经有近1800个新参赛产品被提交,其中甚至有很多因其独具一格的创意而登上技术界媒体的头条,成为人们关注的焦点。在这个平台上引入了许多有趣的概念,不过很多开发者依然认为它只是一个Linux、Java和谷歌自己的应用程序编程接口API的组合产物。本文将通过介绍图片处理相关的编程,来让你明白使用Android平台非常简单,而且这个平台也非常强大。

开始之前的入门知识了解
 
现在关于Android的参考资料并不是很多,而当前最好的资料则来自于谷歌自己的Android的网站:http://code.google.com/android/。在这儿有你所需要的所有必须的开发工具、插件和示例代码,你可以免费下载它们。下载操作很容易,在此不再浪费笔墨介绍。
 
除了在线文档之外,你还可以通过加入社区论坛的方式来得到技术帮助。如果你是初学者,我推荐你选择使用免费的开发工具Eclipse,因为它集成了最好的Android SDK,另外还有调试软件和模拟器。根据很多有经验的开发者介绍,尽管通过命令行或批处理脚本都可以实现创建Android项目,但是使用Eclipse无疑是最容易上手的方法。
 
处理图像的API有哪些?
 
Android的API可以实现很多强大的功能,其中包括:
 
·SQLite结构化数据存储数据库(SQLite for structured data storage):通过它你不用花很大劲就可以在你的应用程序内嵌入一个微型数据库。
·图形库支持:基于OpenGL ES嵌入版的非常好的二维图形库和三维图形库。
·集成Web浏览器支持
·多媒体支持:它支持常用的音频、视频和图像格式。
·谷歌API:映射(Mapping)功能可以让第三方代码显示和控制一个谷歌地图。它还可以通过XMPP支持一个叫做GTalkService的P2P服务。
·硬件相关的支持:有很多人们所期待的功能,用来支持GSM电话、蓝牙、3G、WiFi、定位服务等相关硬件。
 
在谷歌提供的大量API中,我们主要关注下面两个程序包中的相关API:
 
·android.graphics:核心渲染包,它提供了一些初级图形工具,诸如画布、颜色过滤器、画笔等,可以让你直接在屏幕上进行图像处理。
·android.graphics.drawable:编译过的可视化资源用来做背景、标题或屏幕的其他部分。
 
由于图片是位图文件,因此我们将重点了解和使用在android.graphics.Bitmap下的API。
 
文件I/O和支持的图片格式
 
Android支持好几种常见的静态图片格式,例如PNG、JPG和GIF。在本文的示例中,我们将使用JPG格式。如果你考虑使用图片的透视功能,可能选择PNG格式更合适一些。
 
为了从你的软件中查看一个图片文件,你应该将它放在你的软件根目录下的res/drawable目录下。一旦这个图片放在这个文件夹下之后,当你重新编译打包的时候,会自动为它产生一个资源ID。举个例子来说,如果你拥有一个叫做pic1.jpg的图片文件,它将可以通过它的资源ID R.drawable.pic1在程序中被访问。你可以看到这个图像文件扩展名已经被脱去,而R则代表了整体资源文件R.java,它是自动生成的,除非你非常了解这个文件中的资源结构,不推荐你编辑它里面的内容。下面的示例代码介绍如何通过一个图像文件的资源ID来访问它。
 
1 Bitmap mBitmap = BitmapFactory.decodeResource(getResources(),
2    R.drawable.pic1);
3 int pic_width = mBitmap.width();
4 int pic_height = mBitmap.height();
 
如果你希望阅读和编写一个没有指定文件夹结构的图片文件,它应该放在模拟器的/data/data/YourPackageName/files/目录下。举个例子来说,如果你为你的例程创建一个程序包名称为com.cyl.TutorialOnImages。那么当你在运行的时候创建一个新的图片文件,它将被放在/data/data/com.cyl.TutorialOnImages/files/文件夹下。请记住每一个Android应用都将以它自己的用户和组ID来启动,因此你专门设定,某些文件夹是不可以通过你的软件来访问的。下面是一段当你希望输出一个位图到一个output.jpg文件时的代码。
 
1 try {
2    FileOutputStream fos = super.openFileOutput("output.jpg",
3       MODE_WORLD_READABLE);
4
5    mBitmap.compress(CompressFormat.JPEG, 75, fos);
6
7    fos.flush();
8    fos.close();
9    } catch (Exception e) {
10    Log.e("MyLog", e.toString());
11 }
 
图片查看、颜色和透明
 
每一个Android应用应该有一个屏幕布局。你可以在软件内动态的创建它,或者通过一个外部XML文件来指定它。这个文件默认是main.xml。为了包含一个图片,你要使用一个叫做ImageView的查看类。下面是main.xml文件的内容:
 
1 <?xml version="1.0" encoding="utf-8"?>
2
3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
4    android:orientation="vertical"
5    android:layout_width="fill_parent"
6    android:layout_height="fill_parent"
7    >
8
9 <ImageView id="@+id/picview"
10    android:layout_width="wrap_content"
11    android:layout_height="wrap_content"
12    />
13
14 </LinearLayout>
 
如同图片文件可以被通过一个资源ID来访问一样,编译后在全局资源文件R.java中自动为main.xml产生一个叫做R.layout.main的资源ID。下面是当应用程序首次在模拟器上启动时的默认视图。
 
 
图1、软件在模拟器上首次启动
 
每一个图像像素通过一个4字节整数来展现。最高位字节用作alpha通道;换言之用来实现透明/不透明控制。255代表完全不透明;0则代表完全透明。接下来一个字节是red红色通道;255代表完全是红色。依次类推接下来两个字节相应的实现绿色和蓝色通道。
操作图像像素
 
现在你可以对单独的像素进行处理了。通过使用android.graphics.Bitmap API中的getPixels,可以加载像素到一个整数数组中。在本文例子中,你将按照一定规则对每一个像素实现着色。经过这个处理后,所有的像素将被转化为一个范围在0到255的字节码。android.graphics.Bitmap API中的setPixels则用来加载这个整数数组到一个图像中。最后一步是通过ImageView变量mIV来更新屏幕。以下是实现这个染色过程的代码片段。
 
1 private void TintThePicture(int deg) {
2    int[] pix = new int[picw * pich];
3    mBitmap.getPixels(pix, 0, picw, 0, 0, picw, pich);
4
5    int RY, GY, BY, RYY, GYY, BYY, R, G, B, Y;
6    double angle = (3.14159d * (double)deg) / 180.0d;
7    int S = (int)(256.0d * Math.sin(angle));
8    int C = (int)(256.0d * Math.cos(angle));
9
10    for (int y = 0; y < pich; y++)
11    for (int x = 0; x < picw; x++)
12       {
13       int index = y * picw + x;
14       int r = (pix[index] >> 16) & 0xff;
15       int g = (pix[index] >> 8) & 0xff;
16       int b = pix[index] & 0xff;
17       RY = ( 70 * r - 59 * g - 11 * b) / 100;
18       GY = (-30 * r + 41 * g - 11 * b) / 100;
19       BY = (-30 * r - 59 * g + 89 * b) / 100;
20       Y = ( 30 * r + 59 * g + 11 * b) / 100;
21       RYY = (S * BY + C * RY) / 256;
22       BYY = (C * BY - S * RY) / 256;
23       GYY = (-51 * RYY - 19 * BYY) / 100;
24       R = Y + RYY;
25       R = (R < 0) ? 0 : ((R > 255) ? 255 : R);
26       G = Y + GYY;
27       G = (G < 0) ? 0 : ((G > 255) ? 255 : G);
28       B = Y + BYY;
29       B = (B < 0) ? 0 : ((B > 255) ? 255 : B);
30       pix[index] = 0xff000000 | (R << 16) | (G << 8) | B;
31       }
32
33    Bitmap bm = Bitmap.createBitmap(picw, pich, false);
34    bm.setPixels(pix, 0, picw, 0, 0, picw, pich);
35
36    // Put the updated bitmap into the main view
37    mIV.setImageBitmap(bm);
38    mIV.invalidate();
39
40    mBitmap = bm;
41    pix = null;
42 }
 
 
图2:按下中间按钮时的结果
 
实现用户交互操作
 
看完上面的内容后,我们已经知道了如何导入和导出一个图像文件,并且为它创建一个视图,进而在这个图片中处理每一个像素。下面我们要了解的一件事情是实现一个简单的用户交互,这样可以根据用户端的输入来实现相应操作。android.view.KeyEvent中的API让你可以处理响应操作。这个软件被设定为等待方向键的中央键的按键事件。当它被按下的时候,它将为这个图片以增加20的方式来进行着色,并将结果保存的一个文件中。
 
1 public boolean onKeyDown(int keyCode, KeyEvent event) {
2    if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
3
4       // Perform the tinting operation
5       TintThePicture(20);
6
7       // Display a short message on screen
8       nm.notifyWithText(56789, "Picture was tinted",
9          NotificationManager.LENGTH_SHORT, null);
10
11       // Save the result
12       SaveThePicture();
13
14       return (true);
15       }
16
17    return super.onKeyDown(keyCode, event);
18 }
 
一点建议
 
·如果处理所有像素要花费比较长的事件,你可能会得到一个应用程序不响应的弹出对话框。这种情况下,你应该创建一个子线程,在它中实现复杂的计算过程。这样可以让主线程实现无中断运行。
 
·如果这个子线程需要改变主视图(例如对图像进行更新),你应该在主线程中使用一个消息句柄来监听来自子线程的这个消息,并相应的更新视图。一个子线程不能够直接修改主线程中的视图。
 
·你可以进行某些改进。举个例子来说,它可以被修改类从浏览文件夹或从一个URL中读取图像文件。通过多线程和消息处理可以实现图像动画的处理。
 
结束语
 
希望这篇文章能让朋友们对Android平台的工作方式有一个粗略的了解。希望朋友在这个平台上早日创建出自己的应用。
0
相关文章