A couple of important news before I begin, Terrence Barr has written a great new LWUIT introductory article for java.net and its at the top of java.sun.com... Great for introducing people to LWUIT!
Ofir has just passed a significant milestone of 1000 commits into our java.net SVN repository, take into consideration that we had an internal repository where we are passed the 56k version number... Its not strictly LWUIT commits though ;-)
The main subject of this post though is fixed table headers. The default LWUIT table implementation scrolls the title together with the table body, we made that choice since we assume small tables as the main use case. Fixing a column or a row (such as the titles of the table) into place isn't trivial in an efficient way. However, its possible to do something like that by using two tables and keeping their scrolling in sync.
This works great and pretty seamlessly for touch/keyboard usages however the approach has one major drawback that the size of the title might differ from the size of the cells thus causing the table titles to be misaligned from the table columns.
The solution I chose was to hardcode the cell sizes based on the size of the titles, you can obviously adapt that approach to something more appropriate for your need.
LWUIT 1.4 is just around the corner, before the release I wanted to share
Some of the VirtualKeyboard enhancements/improvements that will be released as part of 1.4.
Since the VirtualKeyboard is a pure LWUIT component it can be customized in various ways:
1. Changing the Virtual Keyboard look – All Virtual Keyboard items can be customized from the resource editor, the associated ui id's are:
VKB – this id is used to style the Virtual Keyboard body.
VKBtooltip – this id is used to style the popup tooltip. VKBButton – this id is used to style a regular button on the virtual keyboard (usually a char or a string). VKBSpecialButton – this id is used to style the special buttons such as: 'Space', 'SH', ...
VKBTextInput – this id is used to style the textfield on the virtual keyboard.
2. Adding a language -
The example below demonstrates how to add an input mode that supports hebrew: Create an array of String arrays, each array represents a buttons column.
Now you need to make sure the new HebrewK will be used as the default virtual keyboard. Call this:
VKBImplementationFactory.init(HebrewK.class);
instead of the regular
VKBImplementationFactory.init();
3. Binding a VirtualKeyboard to a TextField – Now we have a use case where a TextField should accept only numbers, therefore launching the regular VirtualKeyboard will be a mistake. What we need to do is to create a 'numbers only' VirtualKeyboard and launch it on a specific TextField.
4. Adding your own button to a TextField – There are several use cases where you would want to place your own buttons on a specific Virtual Keyboard, for example if you are asking the user to insert input for a search field you might want a “search” command instead of the regular “ok” command that will automatically when pressed will invoke a submit action to the network. To accomplish this you need to create a new virtual keyboard, declare your own input buttons and to add your own special button to be part of the virtual keyboard.
Declare a new input with a new special button “Search” (By default Virtual Keyboard is able to understand only the following special keys: "Shift", "Delete", "T9", “Mode”, “Space”, “OK”):
LOST has come to a rather disappointing end so this is probably my last LWUIT themed lost demo... Fortunately due to the size of the lost cast I was able to get a decently large set of names for this particular demo.
I really don't like scrollbars on touch devices, they don't "feel" right especially once you have used a proper touch device (please enough with the resistive displays...). Every now and again we get a "scrollbar type scrolling" in LWUIT request and we always say the same thing "get over it". Scrollbars can't work on small screens, only gestures can...
Then I tried the Android address book... It was illuminating in the sense that it kept the kinetic scroll gestures I love but provided this unintrusive thumb next to the scrollbar that would "appear" when touching the screen and gently fold when you let go of the screen. The cool thing about it is how it reacts to dragging. Unlike the rest of the screen, when you drag the thumb it acts similarly to a scrollbar thumb by dragging in the opposite direction...
This on its own would not seem like a big deal but the cool part is that when you use this method a letter indicating the area of the address book where you are is displayed in the center of the screen!
This allows users to find what they are looking for much faster on Android devices when dragging their thumb, the best part is that it isn't limited just to English and can work for every language! (E.g. the iPhone's right side index of letters doesn't localize well).
I decided I want something like this in LWUIT and implemented it in the code bellow (you can check the LWUIT incubator for the full code), since its purely in LWUIT it will work for all touch devices including J2ME devices such as the Nokia 5230 in the video (a 200 USD unlocked phone!).
As a bonus my version does some things the native Android version doesn't e.g. LWUIT supports screen rotation with this feature and the Android contacts application is always in portrait mode.
There are some requirements such as the list has to be the top level component in a none-scrollable form since it needs to do its own scrolling. I overrode the scrolling behavior when detecting the thumb which is why I had to derive the list. It was also useful for me when writing the letters for the entries.
Other than that the code is relatively simple and shows how you can manipulate LWUIT's scrolling behavior completely without changing a single line of code in LWUIT itself...
/**
* This class must be the top level scrollable to work propely, all of its parent containers
* must be scrollable false!
*
* @author Shai Almog
*/
publicclassThumbListextendsList{
/**
* Delay for the thumb to start "returning" from the moment the user released the touch screen.
*/
privatestaticfinalintTHUMB_SLIDEBACK_DELAY=2200;
/**
* Duration for the slide animation
*/
privatestaticfinalintTHUMB_SLIDE_DURATION=300;
/**
* Indicates whether the thumb image is is showing
*/
privatebooleanthumbShowing=false;
/**
* Flags for thumb slide timeout
*/
privatelongthumbTimerStartTime;
privateintthumbTimer=-1;
/**
* Animation motion returning the thumb to its "place"
*/
privateMotionthumbSlidebackMotion;
/**
* Thumb coordinates on the screen, the X isn't the real X since the width should be added
*/
privateintthumbPositionX;
privateintthumbPositionY;
/**
* Flag indicating that we are now dragging via the thumb and not the gesture
*/
privatebooleanthumbDragMode;
/**
* Background image for the letter displayed on the screen
*/
privateImagetransparentRoundRect;
/**
* Font used for the letter on the screen during thumb drag mode
Its not that LWUIT doesn't support animated GIF's as much as the underlying implementation doesn't support them. MIDP doesn't provide any API's to extract the frames of the animated GIF's and get any indication of when to repaint them. The solution would seem to parse animated GIF's ourselves and paint them ourselves, this isn't trivial since the animated GIF compression and painting logic is rather complex. However, Ugo Chirico seems to have done just that quite a while back! His image class allows loading and displaying an animated GIF within LWUIT and works for many animated GIF's. It probably won't become a part of LWUIT in part due to copyright issues, but I have made some changes to the way he chose to integrate into LWUIT so you don't actually need to change LWUIT in order to integrate with his code. Furthermore, when using my modified LWUIT implementation animated GIF's will be automatically detected and "just work" even within the HTML component and other elaborate use cases in LWUIT. I achieved this by overriding the LWUIT implementation (the VKB version) and all that's necessary to integrate it is to invoke AnimatedGifFactory.install(); before the Display.init() call.
You can get my code from the vprise directory in the LWUIT incubator project, the project includes a small test application within as well.
This can serve as a template to anyone wishing to add additional image support to LWUIT, there are several tricks I used to achieve this:
Image loading requires detecting the image type from the stream, however once I started reading the stream I will need to handle all image types.... To solve this I added BufferedInputStream which guarantees that mark()/reset() would work on the stream and thus I'm able to restore the input stream to its original state in case the image is not a GIF!
I return an internal "native" image, but I don't use MIDP at any point. While I haven't tested it I bet my code works well on Android/RIM since I only use LWUIT logic internally. One would obviously need to modify the classes from which I derive to the appropriate Android/RIM implementations for this to work...
LWUIT animation calls update the state of the GIF, Ugo did this perfectly and I just used the implementation to map directly to his code.