Over a million developers have joined DZone.

Visage Android – Cleaner APIs, Cleaner UIs

· Java Zone

Learn more about Kotlin, a new programming language designed to solve problems that software developers face every day brought to you in partnership with JetBrains.

I have been busily working away at getting Visage ready for developing Android applications. It is a great fit, because Android converts regular Java class files into its special class format, and the Visage compiler happens to generate Java class files. Also, Android is desperately in need of some TLC on their APIs (more on this in a future blog).

So why use Visage for coding Android applications? We do a yearly Hack-a-Thon event at my company (this year called the GXS Xathon). The winning application was written by Tim McNamara, one of my coworkers, and happened to be an Android application.  I decided to do a small port of his code to see if I could improve the maintainability using Visage.  Here is a screenshot of the application:

A base settings page for an application.  It uses a couple edit text fields, several lists, and takes advantage of the summary line to display the current values.  The original version included:

  • A Java PreferenceActivity class file
  • An XML UI layout descriptor
  • Another XML file for array resources


Believe it or not, this is the minimum necessary set of files to create the above screen.  In converting this to Visage, my goal was to get rid of a lot of the redundancy and glue code needed to work across 3 different files.

The end results of the conversion were as follows:


FilesLinesCharacters

Raw Android1 Java, 2 XML2087413
Visage Android1 Visage903640


While the numbers are impressive, what really matters is the code.

Here is the final Visage code for the settings page:

public class Settings extends PreferenceActivity {
    var senderPref:ListPreference;
    var receiverPref:ListPreference;
    var statusPref:ListPreference;
    var pollingPref:ListPreference;
    var passwordPref:EditTextPreference;
    var usernamePref:EditTextPreference;
 
    override var screen = PreferenceScreen { // 1
        preferences: [
            PreferenceCategory { // 1
                "Preferences" // 2
                preferences: [
                    usernamePref = EditTextPreference { // 1...
                        "Username" // 2
                        key: "usernamePref"
                        summary: bind if (usernamePref.text == "") "Currently undefined" else "Current value: {usernamePref.text}" // 3
                    }
                    passwordPref = EditTextPreference {
                        "Password"
                        key: "passwordPref"
                        summary: bind passwordPref.text.replaceAll(".", "*"); // 3
                    }
                    pollingPref = ListPreference {
                        "Polling Interval"
                        key: "pollingPref"
                        defaultValue: "60000"
                        entries: ["30 seconds", "1 minute", "5 minutes", "10 minutes", "15 minutes", "30 minutes", "1 hour"] // 4
                        entryValues: ["30000", "60000", "300000", "600000", "900000", "1800000", "3600000"] // 4
                        summary: bind pollingPref.entry
                    }
                ]
            }
            PreferenceCategory {
                "Filter"
                def CLEAR = "\{Clear Filter\}"; // 5
                preferences: [
                    statusPref = ListPreference {
                        def status = [CLEAR, "HEALTHY", "WARNING", "DOWN"]; // 5
                        "Filter By Status"
                        key: "statusPref"
                        defaultValue: CLEAR
                        entries: status
                        entryValues: status
                        summary: bind if (statusPref.value == CLEAR) "Select a status to filter on." else "Current value: {statusPref.value}"
                    }
                    senderPref = ListPreference {
                        def senders = [CLEAR, for (s in ConnectionService.getSenderNameList()) s];
                        "Filter By Sender"
                        key: "senderPref"
                        defaultValue: CLEAR
                        entries: senders
                        entryValues: senders
                        summary: bind if (senderPref.value == CLEAR) "Select a sender to filter on." else "Current value: {senderPref.value}"
                    }
                    receiverPref = ListPreference {
                        def receivers = [CLEAR, for (r in ConnectionService.getReceiverNameList()) r];
                        "Filter By Receiver"
                        key: "receiverPref"
                        defaultValue: CLEAR
                        entries: receivers
                        entryValues: receivers
                        summary: bind if (receiverPref.value == CLEAR) "Select a receiver to filter on." else "Current value: {receiverPref.value}"
                    }
                ]
            }
        ]
    }
}

While I am not going to include the full original Android code (200 lines is a lot of code!), here are some of the changes that made the Visage version much more succinct (the below bullets match the numbers in the comments above):

  1. The Visage object literal syntax is more concise than XML and just as readable!
  2. Default properties make the object literal syntax even more concise
  3. One bind call can replace dozens of lines of code to setup and instantiate event listeners
  4. No need to declare arrays in a separate file (yes, we have real data types)
  5. This is a real programming language, so you can use constants and variables to repeat arguments (try doing that in XML!)


All of the technology here is real, but getting a completed set of APIs that covers all of the things you can accomplish in Android is still a work in progress.

If you are interested in helping out with this, please join the Visage Developers mailing list.  I will be posting instructions there soon on how to access the bleeding edge Android Visage repository, and contribute to the growing set of Android APIs.

The Java Zone is brought to you in partnership with JetBrains.  Discover how powerful static code analysis and ergonomic design make development not only productive but also an enjoyable experience.

Topics:

Published at DZone with permission of Stephen Chin , DZone MVB .

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}