Developing Metro or Panorama UI for Series 40 full touch
This code example demonstrates how to create a Panorama view (like on Nokia Lumia) for Series 40 touch devices using Nokia UI and Java ME Canvas.
Article Metadata
Code Example
Tested with
Article
Contents |
Introduction
On most mobile UIs, apps are broken into views/pages that fit within the confines of the screen - while parts of other views may be displayed briefly during transitions, essentially each view is separate and discrete. Windows Phone provides an alternative presentation mechanism in which the paged content is displayed on a wide horizontal canvas. For some apps this works really well - allowing the different content on each "page" of content to share the common theme of the app.
This code example recreates the Windows Phone Panorama Control on Series 40 touch devices. The images below show the different screens of the UI used in this example, with the common background theme.
The UI behaviour is very similar to on Window Phones; the main difference on Series 40 is that we need to pay close attention to device limitations and optimise for reduced memory and processing power.
This article also provides a code component, which you can use in your applications for faster development.
Design
Designing a Panorama Page is simple. Its like a vertical sliding level of a game. Only the part which is in between position 0 and 240 (screen width) will be visible on screen, rest are clipped out. Series 40 full touch device have a resolution of 240x400 pixels. We can use Category Bar (new UI component available on Series 40 Developer Platform 2.0) as that of application bar (in Windows Phone).
Since it includes complete canvas drawing, you should take a reference variable for x axis and construct other components depending on that reference variable. For example, If your panorama page contains four panorama items. Use item1X, item2X, item3X and item4X for panorama item1, panorama item2, panorama item3 and panorama item4. See the design specification section for complete specification used in the example provided.
Panorama item is like a container which groups all the related items together. Each panorama item consists of "Title" called Panorama item title. Each item/component (text box, text block, button, list, choice group etc) in a panorama item are drawn on canvas using corresponding x-reference variable.
Metro UI Design Principles
Metro is an design language created by Microsoft. See Design Principles for a walkthrough of the standards and design elements that embody the Metro Design.
UI Components
The Panorama UI is drawn on the Canvas so you cannot use the standard Form UI Components, but instead will need to create your own. I recommend you start by reading the Java Developers Library topic on Canvas.
This article provides a number of "compatible" components that you may find useful, including Tile Buttons and Alerts. I plan to provide other compatible components in the near future.
Tile Button
Important functions of tile MUIButton class are:
- public MUIButton(String content, String imgURL)
- Constructor, Content - button text. imgURL - Url of Image file.
- public void setMUIButtonClickEvent(MUIButtonEvents evt);
- Button Click Event Listener Registration function.
- public MUIButton(String content, String imgURL)
- Constructor, Content - button text. imgURL - Url of Image file.
- public void setMUIButtonClickEvent(MUIButtonEvents evt);
- Button Click Event Listener Registration function.
- public void setPos(int x, int y);
- Places the button on given position with respect to Panorama Item Grids.
- public void setTextColor(int r, int g, int b);
- To customize the text font color. default color white.
- public int[] getTextColor();
- Returns the current text color.
- protected void checkPressed(int x, int y);
- Since it is drawn on canvas, this function is called in pointerPressed event and this will tiger the MUIButtonEvents.
Creating Object of MUIButton is as follows:
MUIButton btn; //Declaration.
btn = new MUIButton("Text", "/metro.png"); //Initialization.
btn.setMUIButtonClickEvent(new MUIButtonEvents() {
public void onPress(MUIButton btn) {
}
}); //Button Click event listener.
btn.setTextColor(0, 0, 255); //To set the text color.
btn.setPos(10, 10); //Position inside PanoramaItem grid.
Detecting button press:
protected void pointerPressed(int x, int y){
for(int i =0; i< super.total ; i++){
panoramaItem p = (panoramaItem) super.pItems.elementAt(i);
if(p.posX == 0 && p.itemNo == 0){
btn.checkPressed(x, y);
}
}
}
Alerts
Important functions of MUIAlert Class are:
- public MUIAlert(String title, String body, int type);
- Constructor, title - Alert dialog title, body - Message to be displayed with alert, type - Button type.
The type can be any of the following:
- MUIAlert_OK
- Alert with only OK button.
- MUIAlert_OKCANCEL
- Alert with OK and Cancel buttons.
- public void setMUIAlertResultListener(MUIAlertResultEventlistener evt);
- Alert result button event listener.
The return result value will be any one of the following depending upon user selection:
- MUIAlert_Result_OK
- Returned on positive selection.
- MUIAlert_Result_CANCEL;
- Returned on negative selection.
Creating Object of MUIAlert is as follows:
MUIAlert = new MUIAlert("Alert", "MetroUI OK Alert!", MUIAlert.MUIAlert_OK);
alt.setMUIAlertResultListener(new MUIAlertResultEventlistener() {
public void onMUIAlertResult(MUIAlert alt, int reuslt) {
}
});
Display.getDisplay(d).setCurrent(alt); //To display on screen. d is instance of a midlet.
Application bar using Category Bar
You can use Category Bar as Application Bar or Task bar for navigation between different pages, if your application consists of more than one page. The article Designing Series 40 apps with Nokia UI API and Canvas for Great User Experiences explains you how to use Category Bar for Navigation (Switching between Displays) and using as Menu bar.
Scrolling with Flick, Selection with Tap and Animation - Gesture and Frame Animator API
Creating List menus, Scroll view and all. Most important the scrolling Panorama page, with which I started discussion. With Gesture API you can detected Tap, Flick, Drag, Drag and Drop events.
If you want to make simple or complex animation with no time, I suggest to use Frame Animator to do animations.
Using Gesture API
Using Frame Animator API, I found Demo Example. But worth of looking at it. You will find most of thing you required.
Design and Implementing Panorama Page
Design specification used in the Example Application
This section describes the main parameters used in the example application provided. These can be used as a guide, although you should use a specification that is suitable to your own app use case. Page
| Object | Image Size | Text |
|---|---|---|
| Background Image | min: h:400, w:240 (for single item) max: h:400, w:240*no_of_item (more than 1) |
no text |
| Page Title | 70x70 pixel | Face: FACE_PROPORTIONAL Style:STYLE_ BOLD Size:SIZE_LARGE |
| Page Title | no image | Face: FACE_PROPORTIONAL Style:STYLE_ BOLD Size:SIZE_LARGE |
| Panorama Item Titles | no image | Face: FACE_MONOSPACE Style:STYLE_ BOLD Size:SIZE_MEDIUM |
| Body | -NA- | Face:FACE_SYSTEM Style:STYLE_PLAIN Size:SIZE_SMALL |
List and List Items
| Object | Image Size | Text |
|---|---|---|
| Icon | Size: 50x50 pixel | -NA- |
| Text 1 | no image | Face:FACE_SYSTEM Style:STYLE_BOLD Size:SIZE_MEDIUM |
| Text 2 | no image | Face:FACE_SYSTEM Style:STYLE_PLAIN Size:SIZE_MEDIUM |
| Text 3 | no image | Face:FACE_SYSTEM Style:STYLE_BOLD Size:SIZE_SMALL |
Sliding the page
Gesture event detection:
public void gestureAction(Object arg0, GestureInteractiveZone arg1,
GestureEvent arg2) {
switch (arg2.getType()) {
case GestureInteractiveZone.GESTURE_FLICK:
float direction = arg2.getFlickDirection(); /* To find out the flick direction*/
boolean left = Math.abs(direction) < Math.PI / 2; /* i.e. right-to-left or left-to-right */
if (left) {
dir = 1; /* 1 because, we need increment in X-axis, to create left-to-right slide effect. */
} else {
dir = -1; /* -1 because, we need decrements in X-axis, to create right-to-left slide effect. */
}
slide = true; /* for handling sliding animation */
slidecount = 0;
break;
case GestureInteractiveZone.GESTURE_TAP: /* this is to detect select event of Misc Playlist item */
m1.pointerPressed(arg2.getStartX(), arg2.getStartY());
m2.pointerPressed(arg2.getStartX(), arg2.getStartY());
m3.pointerPressed(arg2.getStartX(), arg2.getStartY());
default:
break;
}
}
Sliding animation
public void run() {
while (true) {
if (slide) {
if (dir == -1) {/* create right-to-left slide effect. */
if (slidecount != 240) {/* total no of steps to move is 240, i.e screen width */
slidecount ++;
bgPosx --; /*
* Background image x position reference
* variable
*/
item1X --; /*
* Panorama item 1, i.e Music Playlist x
* position reference variable
*/
item2X --; /*
* Panorama item 2, i.e Video Playlist x
* position reference variable
*/
item3X --; /*
* Panorama item 3, i.e About x position
* reference variable
*/
/*
* to create rotating effect. Background Image should
* not scroll/slide beyond the total no. of panorama
* item
*/
if (bgPosx <= -(240 * itemCount)) {
bgPosx = 0;
}
/*
* if the background image width is lesser than the
* length of panorama item, i.e 240*total_item.
*/
/* Background should repeat from beginning of Image */
if (backgroundImage.getWidth() + bgPosx <= 0) {
bgPosx = 240 - slidecount;
}
/*
* To control the position of item and to re-position
* back to original place, when their x position go
* beyond the total items
*/
if (item1X <= -(240 * itemCount)) {
item1X = 0;
item2X = 240;
item3X = 480;
}
if (item2X <= -(240 * itemCount)) {
item2X = 0;
item3X = 240;
item1X = 480;
}
if (item3X <= -(240 * itemCount)) {
item3X = 0;
item1X = 240;
item2X = 480;
}
} else {
/* to create title sliding effect */
titleX -= 45;
if (titleX < -(45 * (itemCount - 1))) {
titleX = 250;
}
slide = false;
}
}
if (dir == 1) {/* create left-to-right slide effect. */
if (slidecount != 240) { /* total no of steps to move is 240, i.e screen width */
slidecount ++;
bgPosx ++;
item1X ++;
item2X ++;
item3X ++;
if (bgPosx > 0) {
bgPosx = -(240 * itemCount);
}
if (backgroundImage.getWidth() + bgPosx <= 0) {
}
if (item1X >= (240 * itemCount)) {
item1X = 0;
item2X = 240;
item3X = 480;
}
if (item2X >= (240 * itemCount)) {
item2X = 0;
item3X = 240;
item1X = 480;
}
if (item3X >= (240 * itemCount)) {
item3X = 0;
item1X = 240;
item2X = 480;
}
} else {
titleX += 45;
slide = false;
}
}
}
if (titleX > 2) {
titleX -= 3;
}
repaint();
System.gc();
}
}
Component
panoramaItem Class: This class contains the structure to create Panorama Items.
- panoramaItem(String title, int itemNo)
- Constructor, title - title of panorama item. itemNo - index of item in the sequence, 0 - first panorama item and (n-1) - last panorama item.
- int posX
- Current x position in the page(Canvas).
- int posY
- Current y position in the page(Canvas). Default value 90.
panoramapage Class: Class extending panoramapage class must create objects of panoramaItem and should initialize the Vector pItems and refer this vector to draw other components in derived class. Derived class of panoramapage class must define these functions:
- protected void pointerPressed(int x, int y)
- protected void paint(Graphics g)
- paint method should call super class paint method. i.e this method should include: super.paint(g);
Important function of panoramapage class should be used in derived class:
- super(String title, String bgImg, String titImg);
- Constructor of super class. title - Page title, bgImg - path to background Image file, titImage - path to title image file.
- super.setPanoramaItems(Vector v);
- Vector v is an Vector containing objects of panoramaItem class. This initializes the Vector pItems of base class.
Important Variable of panoramapage class that will be often used in derived class:
- Vector pItems
- Vector containing panoramaItem.
- int total
- Total number of items in Vector pItems.
Initializing the Vector pItems of base class.
Using Vector pItems and int total to draw components on page:
protected void paint(Graphics g){
super.paint(g);
for(int i =0; i< super.total ; i++){
panoramaItem p = (panoramaItem) super.pItems.elementAt(i);
if(p.posX == 0){
if( p.itemNo == 0){
//draw components belonging to 1st panorama item here.
}else if(p.itemNo == 1) { ... }
}
}
}
Creating Page using Components
public class page extends panoramapage implements MUIButtonEvents, MUIAlertResultEventlistener{
MUIButton btn;
main d;
MUIAlert alt;
public page(String title, String bgImg, String titImg, main d) {
super(title, bgImg, titImg);
this.d = d;
btn = new MUIButton("Text", "/metro.png");
btn.setMUIButtonClickEvent(this);
btn.setTextColor(0, 0, 255);
btn.setPos(10, 10);
String[] tit = {"item 1","item 2","item 3","item 4"};
Vector items = new Vector();
for(int i = 0; i< 4; i++){
items.addElement(new panoramaItem(tit[i], i));
}
super.setPanoramaItems(items);
alt = new MUIAlert("Alert", "MetroUI OK Alert!", MUIAlert.MUIAlert_OK);
alt.setMUIAlertResultListener(this);
}
protected void paint(Graphics g){
super.paint(g);
for(int i =0; i< super.total ; i++){
panoramaItem p = (panoramaItem) super.pItems.elementAt(i);
if(p.posX == 0 && p.itemNo == 0){
btn.paint(g);
}
}
}
protected void pointerPressed(int x, int y){
for(int i =0; i< super.total ; i++){
panoramaItem p = (panoramaItem) super.pItems.elementAt(i);
if(p.posX == 0 && p.itemNo == 0){
btn.checkPressed(x, y);
}
}
}
public void onPress(MUIButton btn) {
Display.getDisplay(d).setCurrent(alt);
}
public void onMUIAlertResult(MUIAlert alt, int reuslt) {
if(alt == this.alt){
if(reuslt == MUIAlert.MUIAlert_Result_OK){
Display.getDisplay(d).setCurrent(d.p1);
}
}
}
}
Source Code: File:SampleMetroUIComponentApp.zip, contains demo application developed using MUI (Mtero UI) components. Example demonstrate the use of MUIButton and MUIAlert also.
Component Source-code
File contains MUIButton, MUIButtonEvents, MUIAlert, MUIAlertResultEventlistener, panoramaItem and panoramapage classes. Extract to "src" folder of your project and its ready to use.
File:Panoramapage.zip
Snapshots
Sample program
Required SDK: Nokia SDK 2.0 for Java Developers
The sample program is developed using "Nokia SDK 2.0 for Java (beta)" with CLDC-1.1 and MIDP 2.1. Please make sure that your target testing project implies the same before copying MIDlet/Class into your project. I suggest you to explore the sample program and use component in your project instead of using the classes from the sample program.
Attached zip file contains a basic scrolling panorama page. Flick right or left and see the amazing: File:MetroApp.zip












Contents
Pooja 1650 - Cool
Can't say anything technically as I am not into S40 but it looks good - concept wise.pooja_1650 10:14, 18 July 2012 (EEST)
Hamishwillee - Feedback
Hi Adarsha
I've re-instated this in the competition and improved the introduction. In particular you will note that the screenshots are now up the top and linked to Panorama control information - this picture provides a much clearer view of what your control is supposed to do than the wireframe, which I've moved down into the "Design" section.
A few specific points:
In terms of documentation, I think this needs work in terms of both style and content. For style, its OK to have the occasional smiley face. However wiki is mostly for hard facts - its not a blog - readers want mostly great information, quickly. I would strip out "If you are with me raise a smile else scroll down! and "So are you thinking what I'm thinking" etc
The content is where I see the most flaws.
To reiterate - what is this section supposed to be teaching?
Overall, as a reader I want to understand
A really "great" article would also provide the code as a "component" that people can reuse. Ie they specify pages and a background to a component, and all the work of layout etc is done for them.
Regards
Hamishhamishwillee 03:58, 24 July 2012 (EEST)
Adarsha saraff - Thanks
Hi Hamish,
Thanks for your valuable feedback. I hope, I had improved at-least 40%. I guess, your are happy with the Component. However, you still need to code, but the component saves most of your time.
-Adarsha.adarsha_saraff 10:21, 25 July 2012 (EEST)
Croozeus - Hey
Hi Adarsha,
I updated the article metadata with SDK and source code. Also moved the article to a shorter title.
Check if OK?
/Pankajcroozeus 08:46, 26 July 2012 (EEST)
Hamishwillee - Still needs better comments
Hi Adarsha
I think Croozeus name change is good, although now you're delivering a component you could call it just "Metro or Panorama UI for Series 40 full touch"
Making this into a reusable component is great. Thanks!
However my points for the documentation still stand - the design section doesn't really cover the design and someone reading this will not learn anything from this. It isn't clear to me what points you're making about the category bar etc.
Also you've added the new component, but not mentioned it in the introductory sections. This is a massive big deal you need to mention up front.
Regards
Hamishhamishwillee 09:13, 27 July 2012 (EEST)
Adarsha saraff - How about now?
Hi Hamish,
I have removed some confusing statements and made it simple. Now I guess, someone reading this will understand a bit?? I am poor in English and writing this much is a big deal for me. :Dadarsha_saraff 22:05, 27 July 2012 (EEST)
Hamishwillee - A little better!
Hi Adarsha
English is tough - but I think you're saying too little because you're hoping the code will speak for you ... and it doesn't (enough) for me. Its great you've introduced the component in the introduction and I think the new image is pretty good. I like that you've said what components can be used. I would say this is quite a lot better, but still some way to go.
First problem is that I still get app error in simulator - do you get this on the version attached? That makes it hard for me to get a great feel for how this works since I can't try it out.
In general I still can't work out what the design sections do for the reader - how they should use this information or how it related to the component. One useful bit is that they have to draw content on the canvas - can they use LWUIT components (also drawn on canvas) as that would make lists etc easier?
In terms of the categorybar - I WAS going to ask when you'd use this - since a category bar implies view based navigation. Perhaps for use on a non-touch device?
In the Component section, you've created the object like this:
panoramapage p1 = new panoramapage("Test Application","/background.png","/title.png");So my questions are
In summary "if this is a reusable component, explain to people what they get out of the box, and what they need to do to extend it".
Regards
Hhamishwillee 08:52, 30 July 2012 (EEST)
Adarsha saraff - Thanks - Fixed a bit.
Hello Hamish,
"In terms of the categorybar - I WAS going to ask when you'd use this - since a category bar implies view based navigation. Perhaps for use on a non-touch device?"
Metro UI design is not suitable for non-touch device, as the principle says "Animation plays a large part, with transitions, and user interactions such as presses or swipes recommended to always be acknowledged by some form of natural animation or motion." As non-touch devices does supports gesture events.
-I have added a note saying this in Introduction.
"In general I still can't work out what the design sections do for the reader - how they should use this information or how it related to the component. One useful bit is that they have to draw content on the canvas - can they use LWUIT components (also drawn on canvas) as that would make lists etc easier?"
I have added the link to UI components, so that you can get Idea about designing components in terms of size (in width and height). -I have added note about this in UI components.
"If I were implementing something like this I'd define a page object that held properties of the page, and was owned by a panorama object that managed it."
Since component is designed to give full freedom of modifying, I have not used any classes to manage item and control. I feel that will restrict the developers in modifying the component, as they need to modify all the supporting classes too. So I left that part for them, so they can implement in their own way.
-I have added some lines about this too.
And regarding Creating of Component Object - I have added comment giving the brief of parameter used.adarsha_saraff 11:47, 30 July 2012 (EEST)
Hamishwillee - Fixed a lot
Hi Adarsha
Actually I think you fixed this quite a lot. I have also further subedited/restructured this to improve the appearance and readability. I particularly like the fact you've provided some UI components that work with your control.
First, some specific feedback:
In general I think this is much improved. I still think that it could be better written to make it easier for a person to do this themselves, but I can't explain further how to do that without doing it myself. Certainly now though the main sections make more sense.
Thank you.
Regards
Hamishhamishwillee 04:30, 31 July 2012 (EEST)
Adarsha saraff - :D
Hello Hamish,
I am going to removes all pointless section, because I need to write more about component and Using component to make their job simple. I have updated the all the files, pls check again, if the problem still arises, let me know.adarsha_saraff 08:16, 31 July 2012 (EEST)