Monday, December 12, 2011

Writing Android UIs with Xtend

(Update: You can find the project on github : https://github.com/svenefftinge/xtend-android-experiments )

Xtend is a great fit for Android development. Since Xtend translates to Java source code, it's very easy to use it. After you've installed the Xtend SDK and the Android Development Tools you only need to do two things to get started.
  1. In Eclipse's preferences configure Xtend's compiler to generated to the gen/ folder.
  2. Add the Xtend lib and Google Guava to your project's classpath. 
Why is it useful?
Using a combination of Xtend's powerful extension methods and closures, it's possible to come up with a very declarative API to define UI models. It takes a couple of minutes to write an API which allows for defining a UI like the following:

class AppActivity extends Activity {
 
  override void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  
    contentView = this.linearLayout [
   
      orientation = VERTICAL
  
      view = this.textView ("Hello Android!")
  
      view = this.button ("Click Me!") [
        onClickListener = [ 
          this.textMessage('Hello you clicked me!').show
        ]
      ]
    ]
  } 
}

The API is made available by means of a static extension import. To outline the idea, here's how the button method is defined:

def static button(Context context, String text,
      (Button)=>void initializer) {
  val result = new Button(context)
  result.text = text
  initializer.apply(result)
  return result
}

Used as an extension method button can be invoked on any instance of Context. Since the last argument is a function type it can be passed after the actual method invocation:

view = this.button ("Click Me!") [
        onClickListener = [ 
          this.textMessage('Hello you clicked me!').show
        ]
      ]

Also note how you can simply assign a closure to onClickListener (which is btw. invoking setOnClickListener()). The compiler will automatically convert it to an abstract class of View.OnClickListener.
And everything is 100% statically typed!



8 Kommentare:

Lateef Jackson said...

How where you able to work around the error:
Error generating final archive: Found duplicate file for APK: .settings/org.eclipse.jdt.core.prefs
eclipse/plugins/com.google.guava_10.0.1.1.jar
eclipse/plugins/com.google.inject_3.0.0.no_no_aop.jar

I can get it to compile and generate the right Java code but not deploy. I can download the basic eclipse and try it since I am using the J2EE eclipse version.

Sven Efftinge said...

I copied the needed jars to the project. Android complaints about duplicate resources. Unfortunately the sometimes exist :-(

I pushed the correctly configured android project to github :
https://github.com/svenefftinge/xtend-android-experiments

Lateef Jackson said...

Thanks Sven that is exactly what I needed!! I was trying to find a duplicate file (looking to try to remove them) but instead I just needed to add the jar as dependencies in the build path.

GergelyJuhasz said...

Hi Sven,

there is a project on github called AndroText (currently under development), which aims to generate android application based on Xtext technology, you could check it out (https://github.com/CMark/androtext), it can generate gui elements, data binding...

Toby said...

I noticed that the R class (generated by Android) is not available inside the Activity. In your example project, if you try to access R::layout::main for example, Xtend gives errors, even though you can add it to the generated Java class without issues. Is there a way around this problem?

Sven Efftinge said...

Hi Toby,
in Xtend you derefernce inner classes with a dollar sign as namespace delimiter. See the project on Github.

https://github.com/svenefftinge/xtend-android-experiments

Satoshi said...

Hi,I'm very interested in developing android app in Xtend.

How does closure convert to interface?
ex:
[this.textMessage('Hello you clicked me!').show]
->
new OnClickListener() {
public void onClick(View v) {
// ...
}
}
Where does method name(ex:'onClick') come from...?

Sven Efftinge said...

Xtend Closures are automatically converted to anonymous classes of the expected type.

That is :

onClickListener = [
this.textMessage('Hello you clicked me!').show
]

really translates to the following Java code:

setOnClickListener(new OnClickListener() {
public void onClick(View it) {
AppActivity.this.textMessage("Hello you clicked me!").show();
}
});