作者:crystal 文章来源:yesky
简介
本设计指南以诺基亚3650为设计原型。一些基于MIDP的移动电话使用移动多媒体API,这些API使MIDlets具有更强大的多媒体能力,尤其是可以支持通过移动电话的照相设备进行图像捕获。
这个指南描述了照相MIDlet,提供了一个简单的使用这种图像捕获能力的示例程序。这种照相MIDlet非常简单特别适合作为使用指南介绍。所以在本指南中提出了一些扩展的方法。
本文假设你假设你已经了解Java编程,同时也了解了基于MIDP的编程,如果您没有这些基础,那么请先学习MIDP编程。
移动多媒体API进行图像捕获
移动多媒体规范包括了怎样使用照相设备捕获图像的代码片断,照相MIDlet使用了同样的技术。
首先必须创建一个用户:
Playerplayer;
…
player=Manager.createPlayer("capture://video");
然后初始化用户:
player.realize();
下面我们需要一个显示图像的video control:
VideoControlvideoControl;
…
videoControl=(VideoControl)player.getControl("VideoControl");
video control有两种显示模式:一种是作为MIDP低层用户接口Canvas对象出现;另一种是作为MIDP高层用户接口窗体控件出现。在这类MIDlet中可以使用Canvas方式,但在第5部分选择执行中需要选择窗体模式。
videoControl.initDisplayMode(VideoControl.USE_DIRECT_VIDEO,canvas);
启动原先创立的Player对象,以便用户可以看见照相头的指示:
player.start();
一旦照相启动,就可以获得一个简单的图像的数据:
byte[]pngData=videoControl.getSnapshot(null);
getSnapshot方法的参数代表获得图像数据的格式,如果该参数为NULL,那么表明获得图像的格式为PNG,这样就保证图像能被所有设备执行。
获得的数据可以由多种方式使用,比如该数据可以存储起来稍后使用,也可以用HTTP方式发送到服务器,也可以立即将其作为一幅图像显示出来,要立即显示可以使用Image对象的createImage(byte[], int, int)方法:
ImagephotoImg=Image.createImage(pngData,0,pngData.length);
图像和图像数据将占有大量内存,所以在不再需要以后要将其释放。
照相MIDlet设计
用户界面设计
照相MIDlet有两种屏幕可供选择:
图1
第一种屏幕是显示镜头前连续的视屏图像,当用户认为镜头图像已经选择好了只需要简单的选择"Capture"或"game action"按钮。
图2
第二种是显示获得的静态照片,为了使文中的MIDlet实例更为简单,所以我没有对图像作任何其他操作,包括存储或发送其他地方。
软件设计
图3 Camera MIDlet类框图
从框图中我们可以清楚的看到CameraMIDlet创建了CameraCanvas 和 DisplayCanvas,CameraCanvas需要一个由MMAPI Manager类创建的图像捕获player并接受一个执行player接口的对象,这个对象需要Video Control并接受一个执行VideoControl接口的对象。你不需要了解更具体的情况,只需通过界面与这些对象的交互就可以了。
示例程序
CameraMIDlet类
MIDlet类控制显示的screen用户界面,当screen需要转变到其他的screen时,screen将回调其自身,screen不能直接与其他screen接触。
//unnamed package
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class CameraMIDletextendsMIDlet{
private CameraCanvascameraCanvas=null;
private DisplayCanvasdisplayCanvas=null;
public CameraMIDlet()
{
}
public void startApp()
{
Displayablecurrent=Display.getDisplay(this).getCurrent();
if(current==null)
{
//firstcallcameraCanvas=newCameraCanvas(this);
displayCanvas=newDisplayCanvas(this);
Display.getDisplay(this).setCurrent(cameraCanvas);
cameraCanvas.start();
}
else{
//returningfrompauseAppif(current==cameraCanvas)
{
cameraCanvas.start();
}
Display.getDisplay(this).setCurrent(current);
}
}
public void pauseApp()
{
if (Display.getDisplay(this).getCurrent()==cameraCanvas)
{
cameraCanvas.stop();
}
}
public void destroyApp(booleanunconditional)
{
if(Display.getDisplay(this).getCurrent()==cameraCanvas)
{
cameraCanvas.stop();
}
}
private void exitRequested()
{
destroyApp(false);
notifyDestroyed();
}
void cameraCanvasExit()
{
exitRequested();
}
void cameraCanvasCaptured(byte[]pngData)
{
cameraCanvas.stop();
displayCanvas.setImage(pngData);
Display.getDisplay(this).setCurrent(displayCanvas);
}
void displayCanvasBack()
{
Display.getDisplay(this).setCurrent(cameraCanvas);
cameraCanvas.start();
}
}
CameraCanvas类
CameraCanvas类创建一个video player对象和一个video control,同时规定了video contro在canvas的显示方式。需要注意的是CameraCanvas对象的paint方法不能重画图像,这个方法只能重画背景和重顶端拖曳的video control,为了阐明screen已经被显示,CameraCanvas使用黄色背景,而DisplayCanvas采用青色背景。
TakeSnapshot方法获取一个快照,并将其转化为图像数据一个PNG格式的字节数组传回到MIDlet,MIDlet再将其传回到DisplayCanvas来显示。
//unnamed package
import javax.microedition.lcdui.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
import java.io.IOException;
class CameraCanvas
extends Canvas
implements CommandListener
{
private final CameraMIDlet midlet;
private final Commandexit Command;
private Command captureCommand=null;
private Player player=null;
private VideoControl videoControl=null;
private bool eanactive=false;
//Two strings for displaying error messages
private String message1=null;
private String message2=null;
CameraCanvas(CameraMID letmidlet)
{
this.midlet=midlet;
exitCommand=newCommand("Exit",Command.EXIT,1);
addCommand(exitCommand);
setCommandListener(this);
try{
player=Manager.createPlayer("capture://video");
player.realize();
//Grab the video control and set it to the current display.
videoControl=(VideoControl)(player.getControl("VideoControl"));
if(videoControl==null)
{
discardPlayer();
message1="Unsupported:";
message2="Can'tgetvideocontrol";
}
else{
videoControl.initDisplayMode(VideoControl.USE_DIRECT_VIDEO,this);
//center video , letting it be clippedifit'stoobig
int canvasWidth=getWidth();
int canvasHeight=getHeight();
int displayWidth=videoControl.getDisplayWidth();
int displayHeight=videoControl.getDisplayHeight();
int x=(canvasWidth-displayWidth)/2;
inty=(canvasHeight-displayHeight)/2;
videoControl.setDisplayLocation(x,y);
captureCommand=newCommand("Capture",Command.SCREEN,1);
addCommand(captureCommand);
}
}
catch(IOExceptionioe)
{
discardPlayer();
message1="IOException:";
message2=ioe.getMessage();
}
catch(MediaExceptionme)
{
discardPlayer();
message1="MediaException:";
message2=me.getMessage();
}
catch(SecurityExceptionse)
{
discardPlayer();
message1="SecurityException";
message2=se.getMessage();
}
}
//Called in case of exception to make sure in valid players are closed
private void discardPlayer()
{
if(player!=null)
{
player.close();
player=null;
}
videoControl=null;
}
publicvoidpaint(Graphicsg)
{
g.setColor(0x00FFFF00);//yellowg.fillRect(0,0,getWidth(),getHeight());
if(message1!=null)
{
g.setColor(0x00000000);//black
g.drawString(message1,1,1,Graphics.TOP|Graphics.LEFT);
g.drawString(message2,1,1+g.getFont().getHeight(),
Graphics.TOP|Graphics.LEFT);
}
}
synchronizedvoidstart()
{
if((player!=null)&&!active)
{
try{
player.start();
videoControl.setVisible(true);
}
catch(MediaExceptionme)
{
message1="Mediaexception:";
message2=me.getMessage();
}
catch(SecurityExceptionse)
{
message1="SecurityException";
message2=se.getMessage();
}
active=true;
}
}
synchronizedvoidstop()
{
if((player!=null)&&active)
{
try{
videoControl.setVisible(false);
player.stop();
}
catch(MediaExceptionme)
{
message1="MediaException:";
message2=me.getMessage();
}
active=false;
}
}
public void commandAction(Commandc,Displayabled)
{
if(c==exitCommand)
{
midlet.cameraCanvasExit();
}
elseif(c==captureCommand)
{
takeSnapshot();
}
}
public void keyPressed(intkeyCode)
{
if(getGameAction(keyCode)==FIRE)
{
takeSnapshot();
}
}
private void takeSnapshot()
{
if(player!=null)
{
try{
byte[]pngImage=videoControl.getSnapshot(null);
midlet.cameraCanvasCaptured(pngImage);
}
catch(MediaExceptionme)
{
message1="MediaException:";
message2=me.getMessage();
}
}
}
}
DisplayCanvas类
DisplayCanvas类的作为就是将接受自MIDlet的字节数组转变成图像并显示在屏幕上。
//unnamed package
import javax.microedition.lcdui.*;
class DisplayCanvas
extends Canvas
implements CommandListener{
private final CameraMIDletmidlet;
private Image image=null;
DisplayCanvas(CameraMIDlet midlet)
{
this.midlet=midlet;
addCommand(newCommand("Back",Command.BACK,1));
setCommandListener(this);
}
public void paint(Graphicsg)
{
g.setColor(0x0000FFFF);//cyan
g.fillRect(0,0,getWidth(),getHeight());
if(image!=null)
{
g.drawImage(image,
getWidth()/2,
getHeight()/2,
Graphics.VCENTER|Graphics.HCENTER);
}
}
void setImage(byte[]pngImage)
{
image=Image.createImage(pngImage,0,pngImage.length);
}
public void commandAction(Commandc,Displayabled)
{
midlet.displayCanvasBack();
}
}
应用程序描述符(camera.jad)
Camera MIDlet的应用程序描述符仅仅是简单的将MIDlet描述加入到托管域中。
MIDlet-Description:CameraMIDlet
0MIDlet-Jar-Size:4186
MIDlet-Jar-URL:camera.jar
MIDlet-Name:Camera
MIDlet-Vendor: Forum Nokia
MIDlet-Version:0.4
Manifest
Manifest包括了加入描述的托管域和一个icon()函数:
MIDlet-Name:Camera
MIDlet-Version:0.4
MIDlet-Vendor:Forum Nokia
MIDlet-Description:Camera
MIDletMIDlet-Icon:/camera.png
MicroEdition-Profile:MIDP-1.0
MicroEdition-Configuration:CLDC-1.0
MIDlet-1:Camera,/camera.png,CameraMIDlet
使用VideoControl表单
CameraCanvas类常用来显示采集到的图像,他所起的作用与常见的表单大致相同,一旦执行它就将取代CameraCanvas(对CameraMIDlet作了很少的微不足道的修改),代码片断如下:
CameraForm类
//unnamed package
import javax.microedition.lcdui.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
import java.io.IOException;
class CameraForm
extends Form
implements CommandListener{
private final FormCameraMIDlet midlet;
private final Command exitCommand;
private Command captureCommand=null;
private Player player;
private VideoControl videoControl;
private Boolean active=false;
private StringItem messageItem;
CameraForm(FormCameraMIDlet midlet)
{
super("Camera");
this.midlet=midlet;
messageItem=newStringItem("Message","start");
append(messageItem);
exitCommand=newCommand("Exit",Command.EXIT,1);
addCommand(exitCommand);
setCommandListener(this);
//initialize camera
try{
player=Manager.createPlayer("capture://video");
player.realize();
//Grab the video control and set it to the current display.
videoControl=(VideoControl)(player.getControl("VideoControl"));
if(videoControl!=null)
{
append((Item)(videoControl.initDisplayMode(
VideoControl.USE_GUI_PRIMITIVE,null)));
captureCommand=newCommand("Capture",Command.SCREEN,1);
addCommand(captureCommand);
messageItem.setText("OK");
}
else{
messageItem.setText("Novideocontrol");
}
}
catch(IOExceptionioe)
{
messageItem.setText("IOException:"+ioe.getMessage());
}
catch(MediaExceptionme)
{
messageItem.setText("MediaException"+me.getMessage());
}
catch(SecurityExceptionse)
{
messageItem.setText("Securityexception:"+se.getMessage());
}
}
synchronizedvoidstart()
{
if(!active)
{
try{
if(player!=null)
{
player.start();
}
if(videoControl!=null)
{
videoControl.setVisible(true);
}
}
catch(MediaExceptionme)
{
messageItem.setText("Mediaexception:"+me.getMessage());
}
catch(SecurityExceptionse)
{
messageItem.setText("Securityexception:"+se.getMessage());
}
active=true;
}
}
synchronizedvoidstop()
{
if(active)
{
try{
if(videoControl!=null)
{
videoControl.setVisible(false);
}
if(player!=null)
{
player.stop();
}
}
catch(MediaExceptionme)
{
messageItem.setText("Mediaexception:"+me.getMessage());
}
active=false;
}
}
public void commandAction(Commandc,Displayabled)
{
if(c==exitCommand)
{
midlet.cameraFormExit();
}
elseif(c==captureCommand)
{
//if we have a capture command,we know videoControlisnotnulltry{
byte[]pngImage=videoControl.getSnapshot(null);
midlet.cameraFormCaptured(pngImage);
messageItem.setText("OK");
}
catch(MediaExceptionme)
{
messageItem.setText("Mediaexception:"+me.getMessage());
}
}
}
}
结束语
本文提供的MIDlet仅仅是一个实例,在实践中可以扩展这个实例。首先在实践中可以将图像存储起来,然后在其他屏幕浏览这些图像,这是一个非常有意思的做法。另外还可以将图像以HTTP方式传送到WEB服务器上,然后可以将这些图像动态的加入用户的WEB日志中,MIDlet也允许用户将带文本的图像传送到WEB站点。