Android Meets Ruby: Using Ruboto UI
Ruboto, the project that has brought jruby scripting to Android devices, continues to improve, and has made it easier to concisely define new user interfaces for your scripted Android apps. The traditional method of writing UIs within the script has been improved to support more natural customization of components. Alternately, you can take advantage of existing Java libraries to use Android-style XML layouts for your interface.
A quick facelift
We’ll start out by taking advantage of some syntactic upgrades. An app I wrote for a previous version of Ruboto allowed ordering fresh fish by placing an SMS message, and took advantage of existing Ruby libraries for natural-language processing. We’ll now create a competitor’s app, seeking to capitalize on this growing market and supporting more dynamic updateable interfaces. We’ll first rewrite the content view-creation to look like the following.
def on_create(bundle) self.content_view = linear_layout(:orientation => :vertical) do @text_view = text_view :text => 'What would you like to order?' @group = radio_group do radio_button :text => "Tuna", :id => 0 radio_button :text => "Trout", :id=> 1 radio_button :text => "Salmon", :id => 2 radio_button :text => "Crab", :id => 3 radio_button :text => "Lobster", :id => 4 end @quantity = edit_text :hint => "Quantity" button :text => "Place Order", :on_click_listener => @handle_click end end
Even without changing any functionality, we’ve already made a few improvements over the original version of this app:
- The new way of configuring a layout is more natural and Ruby-like, using linear_layout(:orientation => :vertical) instead of the more cumbersome Java-like linear_layout :orientation => LinearLayout::VERTICAL
- More importantly, instead of handling all clicks through a single Activity-wide handle_click, we can now configure the click handler for each individual component. This will make larger apps far more maintainable.
Running this app gives us a screen like the following:
Since Ruboto is usually praised for being a lightweight and flexible framework, it can be a bit jarring to look at its UI code. Where an Android app will have a single call to setContentView that points to a markup XML resouce, Ruboto apps have typically used dozens or more lines of boilerplate code to programatically build up their user interfaces. The syntax is familiar to Ruby developers, but the philosophy is not; it’s rarely a good practice to embed your presentation layer within your code. Some developers, particularly those coming from the Android world, may prefer to separate out their presentation.
Fortunately, doing so is quite straightforward, thanks to the pluggable nature of Ruboto (which can be supplemented by any Java or Ruby code), and the existence of other frameworks for creating UIs. One very straightforward approach is to integrate the ViewInflater from DroidDraw, an open-source project that specializes in a WYSIWYG editor for Android interfaces but can serve Ruboto quite nicely by letting us inflate XML resources for the UI. By using DroidDraw, we can duplicate most of the functionality in Android’s XML UI layouts, but we don’t need to precompile the XML as a resource as is usually done for Android projects.
To make an XML inflation within our app, we just need to do the following:
- Download the ViewInflater class. Check to make sure you have a recent version; older ones are missing the version of inflate() that takes a String argument. Copy this into your Ruboto project’s Java source tree, at /src/org/droiddraw/android. (On my current setup, I found that I needed to perform a “rake clean” followed by a “rake” and “adb install” in order for the ViewInflater to get added to my APK; shortcut commands like “rake install” did not work and would lead to runtime class not found errors.)
- Import the inflater class into your script:
- You can replace your on_create with new code to retrieve or build the display XML; create a ViewInflater that takes that XML; and then set the result as your content view. The user interface we built above could be replicated with the following:
inflater = ViewInflater.new($activity) text = "<?xml version='1.0' encoding='utf-8'?>" + "<LinearLayout " + "xmlns:android='http://schemas.android.com/apk/res/android' " + "android:layout_width='fill_parent' " + "android:layout_height='fill_parent'" + "android:orientation='vertical' >" + "<TextView android:text='What would you like to order?'/>" + "<RadioGroup>" + "<RadioButton android:text='Tuna' android:id='0' />" + "<RadioButton android:text='Trout' android:id='1' />" + "<RadioButton android:text='Salmon' android:id='2' />" + "<RadioButton android:text='Crab' android:id='3' />" + "<RadioButton android:text='Lobster' android:id='4' />" + "</RadioGroup>" + "<EditText android:hint='Quantity' android:layout_width='fill_parent' />" + "<Button android:text='Place Order' />" + "</LinearLayout>" view = inflater.inflate(text) $activity.setContentView view
Of course, in an actual app, you would load the XML from your app’s assets, download them from a server, or load from the device filesystem. This approach can potentially combine the best of all worlds: you can use a familiar markup language to craft your app’s UI, and can dynamically select and load those UIs at runtime.
There are a few limitations with this approach to keep in mind. First of all, it loses the Ruboto benefit of creating references to your UI elements as you declare them; instead, you’ll need to use an Android-style approach of calling findViewById to retrieve references to your UI objects if you need to interact with them later. Secondly, you’re limited to the current behavior of your view inflater code; for example, DroidDraw currently does not expose the “android:hint” UI property on an EditText element, so your hint won’t display. (The flip side, of course, is that since you have access to the source code, you can trivially add support for any important attributes.)
If you run the updated script, you should see the following screen.
You’ll notice some minor differences from the first version. Specifically, the Ruboto version of the <Button> widget applies a width of match_parent by default, while the DroidDraw version applies wrap_content by default. In both cases, you can override the defaults to match your preference.
The road ahead
Ruboto continues to evolve and grow; it has made dramatic strides over the past year, and as of this writing, its developers are preparing to release version 0.6, which will bring still more improvements to developers. As Ruboto matures, more developers will be able to take advantage of its immense promise of fully dynamic Android applications.
Safari Books Online has the content you need
Check out these Android books available from Safari Books Online:
|Android in Action, Third Edition is a fast-paced book that puts you in the driver’s seat–you’ll master the SDK, build WebKit apps using HTML 5, explore cross-platform graphics with RenderScript, learn to use Native Development Kit, and master important tablet concepts like drag-and-drop, fragments, and the Action Bar, all new in Android 3.|
|Beginning Android 4 is fresh with details on the latest iteration of the Android platform. Begin at the beginning by installing the tools and compiling a skeleton app. Move through creating layouts, employing widgets, taking user input, and giving back results.|
|Android in Practice is a treasure trove of Android goodness, with over 90 tested, ready-to-use techniques including complete end-to-end example applications and practical tips for real world mobile application developers. The book dives into important topics like multitasking and services, testing and instrumentation, building and deploying applications, and using alternative languages.|
|Android UI Fundamentals: Develop and Design walks developers through the different choices available on their way to creating a well-designed application for Android. While building a simple application, Jason works through the basics of Android UI development including layout, event handling, menus and notifications.|
|Programming Android shows experienced application developers what they need to program for the Android operating system — the core building blocks, how to put those blocks together, and how to build compelling apps that work on a full range of Android devices.|
|Whether you want to develop a commercial application for mobile devices, or just want to create a mobile mashup for personal use, Android Application Development demonstrates how you can design, build, and test applications for the new mobile market|
About this author
|Chris King is a software engineer specializing in mobile development. He is the author of Advanced BlackBerry Development, Advanced BlackBerry 6 Development, and Android in Action, Third Edition. You can learn more about Chris at www.linkedin.com/pub/chris-king|