Wednesday, July 7, 2010

Swipe That Image

The touch device Swipe gesture has been well supported on LWUIT from day one however strictly for the purpose of scrolling. Its pretty easy and common to implement the swipe for other uses such as paging through pictures but there users often want immediate feedback which is a bit harder.

Novices to LWUIT might think it makes sense to try and combine the swipe and transition, but that is probably not so practical since the transitions assume speed and structure (partially because they are generic and can slide between anything)...



This demo shows off Yoga posture pictures taken from beyoga.co.il (copyrighted!) in such a slide demo on the HD2. The code works well on pretty much any device with or without touch and is actually really simple to implement. I will commit it to my incubator directory soon:



public class BrowserMIDlet extends MIDlet implements Runnable {

private static final String[] IMAGE_NAMES = {

"http://beyoga.co.il/beyoga/gallery/asana/mainColumnParagraphs/0/subPar/03/image/19.JPG",

"http://beyoga.co.il/beyoga/gallery/asana/mainColumnParagraphs/0/subPar/08/image/setu_bandhasana.jpg",

"http://beyoga.co.il/beyoga/gallery/asana/mainColumnParagraphs/0/subPar/02/image/Kukkutasana.jpg",

"http://beyoga.co.il/beyoga/gallery/asana/mainColumnParagraphs/0/subPar/016/image/parighasana.jpg",

"http://beyoga.co.il/beyoga/gallery/asana/mainColumnParagraphs/0/subPar/06/image/Kapotasana.jpg",

"http://beyoga.co.il/beyoga/gallery/asana/mainColumnParagraphs/0/subPar/014/image/24.jpg",

"http://beyoga.co.il/beyoga/gallery/asana/mainColumnParagraphs/0/subPar/07/image/18.jpg"

};



private Image[] slides;

private Form images;





public void startApp() {

try {

Display.init(this);

Resources r = Resources.open("/LWUITtheme.res");

UIManager.getInstance().setThemeProps(r.getTheme(r.getThemeResourceNames()[0]));

images = new Form("Be Yoga");

images.setLayout(new BorderLayout());

Image progress = Image.createImage("/progress0.png");

AnimationObject[] animations = new AnimationObject[] { AnimationObject.createAnimationImage(progress, 0, 0) };

animations[0].defineOrientation(AnimationObject.MOTION_TYPE_LINEAR, 0, 1500, 0, 359);

Timeline t = Timeline.createTimeline(1500, animations, new Dimension(progress.getWidth(), progress.getHeight()));

images.addComponent(BorderLayout.NORTH, new Label("Loading Images Please Wait..."));

images.addComponent(BorderLayout.CENTER, new Label(t));

images.show();

new Thread() {

public void run() {

// download the images from the web site

slides = new Image[IMAGE_NAMES.length];

for(int iter = 0 ; iter < IMAGE_NAMES.length ; iter++) {

InputStream i = null;

try {

i = Connector.openInputStream(IMAGE_NAMES[iter]);

slides[iter] = EncodedImage.createImage(i);

i.close();

} catch (IOException ex) {

ex.printStackTrace();

Dialog.show("Error", ex.getMessage(), "Exit", null);

BrowserMIDlet.this.notifyDestroyed();

} finally {

try {

i.close();

} catch (IOException ex) {

ex.printStackTrace();

}

}

}

Display.getInstance().callSerially(BrowserMIDlet.this);

}

}.start();

} catch (IOException ex) {

ex.printStackTrace();

}

}



public void run() {

images.removeAll();

images.addComponent(BorderLayout.CENTER, new Label() {

private int pos = 0;

private int dest = 1;

private int initialX = 0;

private int currentX = 0;

private Image currentImage = null;

private Image destImage = null;

private int currentImageOffset = 0;

private int destImageOffset = -1;

private Motion slideToDestMotion;



{

setFocusable(true);

requestFocus();

}



public boolean animate() {

boolean b = super.animate();

if(slideToDestMotion != null) {

currentX = slideToDestMotion.getValue();

if(slideToDestMotion.isFinished()) {

slideToDestMotion = null;

int dist = currentX - initialX;

if(dist != 0) {

if(Math.abs(dist) > getWidth() / 2) {

if(dist > 0) {

pos = pos - 1;

if(pos < 0) {

pos = slides.length - 1;

}

} else {

pos = pos + 1;

if(pos == slides.length) {

pos = 0;

}

}

dest = pos;

}

}

currentX = 0;

initialX = 0;

getComponentForm().deregisterAnimated(this);

}

return true;

}

return b;

}



private Image getCurrentImage() {

if(pos == currentImageOffset && currentImage != null && currentImage.getWidth() == getWidth()) {

return currentImage;

}

currentImage = slides[pos].scaledSmallerRatio(getWidth(), getHeight());

currentImageOffset = pos;

return currentImage;

}



private Image getDestImage() {

if(dest == destImageOffset && destImage != null && destImage.getWidth() == getWidth()) {

return destImage;

}

destImage = slides[dest].scaledSmallerRatio(getWidth(), getHeight());

destImageOffset = dest;

return destImage;

}



public void keyReleased(int c) {

int g = Display.getInstance().getGameAction(c);

if(slideToDestMotion == null) {

switch(g) {

case Display.GAME_LEFT:

dest = pos - 1;

if(dest < 0) {

dest = slides.length - 1;

}

slideToDestMotion = Motion.createSplineMotion(0, getWidth() + initialX, 200);

slideToDestMotion.start();

getComponentForm().registerAnimated(this);

break;

case Display.GAME_RIGHT:

dest = pos + 1;

if(dest == slides.length) {

dest = 0;

}

slideToDestMotion = Motion.createSplineMotion(0, -getWidth(), 200);

slideToDestMotion.start();

getComponentForm().registerAnimated(this);

break;

}

}

super.keyReleased(c);

}



public void pointerPressed(int x, int y) {

initialX = x;

currentX = x;

}



public void pointerDragged(int x, int y) {

currentX = x;

int dist = currentX - initialX;

if(dist != 0) {

if(dist > 0) {

dest = pos - 1;

if(dest < 0) {

dest = slides.length - 1;

}

} else {

dest = pos + 1;

if(dest == slides.length) {

dest = 0;

}

}

}

repaint();

}



public void pointerReleased(int x, int y) {

int dist = currentX - initialX;

if(dist != 0) {

if(Math.abs(dist) > getWidth() / 2) {

if(dist > 0) {

slideToDestMotion = Motion.createSplineMotion(currentX, getWidth() + initialX, 250);

} else {

slideToDestMotion = Motion.createSplineMotion(currentX, initialX - getWidth(), 250);

}

} else {

slideToDestMotion = Motion.createSplineMotion(currentX, initialX, 250);

}

slideToDestMotion.start();

getComponentForm().registerAnimated(this);

}

repaint();

}



public void paint(Graphics g) {

Image currentImg = getCurrentImage();

int currentImgX = getX() + (getWidth() - currentImg.getWidth()) / 2;

int currentImgY = getY() + (getHeight() - currentImg.getHeight()) / 2;

if(currentX == initialX) {

g.drawImage(currentImg, currentImgX, currentImgY);

} else {

Image destImg = getDestImage();

int dist = currentX - initialX;

g.drawImage(currentImg, currentImgX + dist, currentImgY);

int destImgX = getX() + (getWidth() - destImg.getWidth()) / 2;

int destImgY = getY() + (getHeight() - destImg.getHeight()) / 2;

if(dist > 0) {

g.drawImage(destImg, destImgX - getWidth() + dist, destImgY);

} else {

g.drawImage(destImg, destImgX + getWidth() + dist, destImgY);

}

}

}

});

images.revalidate();

}



public void pauseApp() {

}



public void destroyApp(boolean unconditional) {

}

}



4 comments:

  1. Hi, I'm new in blog. In libray of LWUIT I don't have AnimationObject and Timeline. Can You tell me why?

    ReplyDelete
  2. You need the latest LWUIT from SVN to run the code in the blog

    ReplyDelete
  3. Hi
    I tried above code for Nokia S60 5th edition but i am getting
    OutOfMemoryError. I am using Netbeans. I changed java heap size of project but no luck. what could be the solution for this ?

    ReplyDelete
  4. You can't change the heap size on a mobile phone or an emulator. I'm guessing you are opening an image from the camera which most MIDP phones don't support since the camera might be 8mp which requires 32mb in RAM but J2ME is only allocated 8mb or so.
    There is nothing we can do about that since this is an inherent failure of device manufacturers.

    ReplyDelete