作者: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类的作为就是将接受自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站点。