Panorama
is the MIDlet main class.
Import the required
classes. This example uses the common MIDlet and some LCDUI classes,
and a number of Mobile Sensor imports. The DeviceControl
class from the Nokia UI
API is used to keep the backlight on continuously.
import java.io.IOException; import javax.microedition.io.Connector; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Image; import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException; import javax.microedition.sensor.ChannelInfo; import javax.microedition.sensor.Data; import javax.microedition.sensor.DataListener; import javax.microedition.sensor.SensorConnection; import javax.microedition.sensor.SensorInfo; import javax.microedition.sensor.SensorManager; import com.nokia.mid.ui.DeviceControl;
Create the main
class and initial parameters to be used in the example. iCanvas
is an instance of PanoramaCanvas
, which is created later.
public class Panorama extends MIDlet implements DataListener, PanoramaCanvas.ExitIf { static final int BUFFER_SIZE = 3; private static PanoramaCanvas iCanvas; private static SensorConnection iConnection; private static final String PHOTO_NAME = "/for_more_see_roundus_dot_com.jpg"; public Panorama() { }
Create the data
listening method. Note the reference to the setPosition
method, which is created
in PanoramaCanvas
.
public void dataReceived( SensorConnection con, Data[] aData, boolean aMissed) { iCanvas.setPosition(aData); }
Create a method
for exiting the application. This is received from the PanoramaCanvas.ExitIf
method that was extended in step 1.
public void exit() { try { destroyApp( true ); } catch (MIDletStateChangeException e) { e.printStackTrace(); } notifyDestroyed(); }
Create the mandatory
MIDlet methods. startApp
initializes PanoramaCanvas
and sets it as the current Display
, opens the sensor connection, and avoids backlight fadeout is by
using DeviceControl.setLights
.
protected void destroyApp(boolean arg0) throws MIDletStateChangeException { try { if (iConnection!=null) iConnection.close(); } catch (IOException e) { e.printStackTrace(); } } protected void pauseApp() { } protected void startApp() throws MIDletStateChangeException { DeviceControl.setLights( 0, 100 ); Display disp = Display.getDisplay( this ); try { iCanvas = new PanoramaCanvas( Image.createImage(PHOTO_NAME), this ); disp.setCurrent( iCanvas ); iConnection = openAccelerationSensor(); if (iConnection != null) iConnection.setDataListener( this, BUFFER_SIZE ); } catch (IOException e) { e.printStackTrace(); } }
Create a method
for opening the sensor connection. As devices based on the Symbian
platform have two data return formats for acceleration sensors, the
following method selects the correct one by its data format (integer
in this case) with the ChannelInfo.TYPE_INT
parameter.
private SensorConnection openAccelerationSensor(){ SensorInfo[] infos = SensorManager.findSensors("acceleration", null); if (infos.length==0) return null; // INT data type is preferred int i=0; for (i=0; i<infos.length && infos[i].getChannelInfos()[0].getDataType()!=ChannelInfo.TYPE_INT; i++); try{ return i==infos.length ? (SensorConnection)Connector.open(infos[0].getUrl()): (SensorConnection)Connector.open(infos[i].getUrl()); }catch (Exception e) { return null; } } }
PanoramaCanvas
determines whether the image
is in portrait or landscape orientation and calculates the image scrolling
from the orientation changes of the device. It is also possible to
traverse the image with arrow key presses.
Import the needed
classes. PanoramaCanvas
mostly uses the LCDUI Canvas
and related classes.
import java.io.IOException; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image; import javax.microedition.sensor.Data; import com.nokia.mid.ui.DeviceControl;
Set the required variables.
public class PanoramaCanvas extends Canvas { private static final int STEP = 5; private static ExitIf iExit; private static Image iImage; private static int iImageHeight; private static int iImageWidth; private static int x = 0; private static int y = 0; private static int ii = 0;
Continue setting
values for the parameters. Add a repaint
method for drawing the changes
on the screen.
public PanoramaCanvas( Image aImage, ExitIf aExit) throws IOException { super(); setFullScreenMode( true ); iExit = aExit; iImage = aImage; iImageHeight = iImage.getHeight(); iImageWidth = iImage.getWidth(); y=-(iImageHeight-getHeight())/ 2; repaint(); }
Add a method to use the received accelerometer values and react to them by moving the image on the screen. In this example, full screen mode is also forced by default.
void setPosition(Data[] aData){ if (ii++%100==0){ DeviceControl.setLights(0, 100); //to keep backlight on setFullScreenMode( true ); } x = getX(x+=getX(aData)); y = getY(y+=getY(aData)); repaint(); }
Add functions
to deal with directional key presses. In this case, all key presses
scroll the image as much as is set in the STEP
variable. keyRepeated
handles a similar function
for held down keys.
/** * deal with any key presses */ protected void keyPressed(int keyCode) { System.out.println("keyCode =" +keyCode); switch(keyCode){ case -1: //up case Canvas.UP: y = getY(y+=STEP); break; case -2: //down case Canvas.DOWN: y = getY(y+=-STEP); break; case -3: //left case Canvas.LEFT: x = getX(x+=STEP); break; case -4: //right case Canvas.RIGHT: x = getX(x+=-STEP); break; default: } repaint(); } protected void keyRepeated(int keyCode){ keyPressed(keyCode); }
Create the paint
method to move the image on the Canvas
.
protected void paint( Graphics g ){ g.drawImage( iImage, x , y, Graphics.TOP | Graphics.LEFT ); if( x + iImageWidth - getWidth() < 0 ) g.drawImage( iImage, x + iImageWidth, y, Graphics.TOP | Graphics.LEFT ); }
Create a few
additional functions. Here, pointerPressed
is
set to activate the exit so that the user can exit the MIDlet by pressing
on the Selection Key or tapping on the screen, and sizeChanged
is called to center the image on the screen in cases where a size
change occurs.
protected void pointerPressed(int x, int y) {iExit.exit();} protected void sizeChanged(int w, int h) { y=-(iImageHeight-getHeight())/ 2; repaint(); }
Detect the image size and create the image scrolling functionality accordingly.
private int getX(Data[] aData){ int x_axis = 0; boolean isPortrait = getHeight()>getWidth(); int index= isPortrait? 0: 1; try{ for (int i=0; i<Panorama.BUFFER_SIZE; i++){ x_axis += aData[index].getIntValues()[0]; } x_axis = (int)(x_axis/Panorama.BUFFER_SIZE); }catch (IllegalStateException e) { for (int i=0; i<Panorama.BUFFER_SIZE; i++){ x_axis += (int)aData[index].getDoubleValues()[0]; } x_axis = (int)(x_axis/Panorama.BUFFER_SIZE); } return isPortrait?-x_axis%iImageWidth:x_axis%iImageWidth; } private int getY(Data[] aData){ int y_axis = 0; boolean isPortrait = getHeight()>getWidth(); int index= isPortrait? 1: 0; try{ for (int i=0; i<Panorama.BUFFER_SIZE; i++){ y_axis += aData[index].getIntValues()[0]; } y_axis = (int)(y_axis/Panorama.BUFFER_SIZE); }catch (IllegalStateException e) { for (int i=0; i<Panorama.BUFFER_SIZE; i++){ y_axis += (int)aData[index].getDoubleValues()[0]; } y_axis = (int)(y_axis/Panorama.BUFFER_SIZE); } return y_axis%iImageHeight; } private int getX(int x){ x = x>0?-iImageWidth+x:x; return x % iImageWidth; } private int getY(int y){ y = y>0?0:y; // upper limit return Math.abs(y)>iImageHeight-getHeight()?getHeight()-iImageHeight:y; //bottom limit }
Set the ExitIf
interface to call exit
. ExitIf
is used in the Panorama.destroyApp
method.
static interface ExitIf { public void exit(); }; }