当前位置:SEARU.ORG > Linux 软件 > 正文

Android特定语言 Xtendroid

Xtendroid是一款Android的领域特定语言,它大大降低样板代码,同时提供巨大的工具支持。Xtendroid利用Xtend transpiler实现,它的特点是能够在Java代码编辑或编译期间具有拓展方法和活动注释(实时编辑代码生成器)功能。活动注释,他特别能够让Xtend比Kotlin或者Groovy语言更加适合DSL的创建。Xtendroid支持的Eclipse和IntelliJ/ Android Studio,其中包括代码完成,调试等。


Anonymous inner classes (lambdas)

Android code:

// get button widget, set onclick handler to toast a message
Button myButton = (Button) findViewById(;
myButton.setOnClickListener(new View.OnClickListener() {
   public void onClick(View v) {
      Toast.makeText(this, "Hello, world!", Toast.LENGTH_LONG).show();

Xtendroid code:

import static extension org.xtendroid.utils.AlertUtils.* // for toast(), etc.
// myButton references getMyButton(), a lazy-getter generated by @AndroidActivity
myButton.onClickListener = [View v|    // Lambda - params are optional
   toast("Hello, world!")

Note: Semi-colons optional, compact and differentiating lambda syntax, getters/setters as properties.

Type Inference

Android code:

// Store JSONObject results into an array of HashMaps
ArrayList<HashMap<String,JSONObject>> results = new ArrayList<HashMap<String,JSONObject>>();
HashMap<String,JsonObject> result1 = new HashMap<String,JSONObject>();
result1.put("result1", new JSONObject());
result2.put("result2", new JSONObject());

Xtendroid (Xtend) code:

var results = #[
    #{ "result1" -> new JSONObject },
    #{ "result2" -> new JSONObject }

Note: compact notation for Lists and Maps, method call brackets are optional


Blink a button 3 times (equivalent Android code is too verbose to include here):

import static extension org.xtendroid.utils.AsyncBuilder.*
// Blink button 3 times using AsyncTask
async [
    // this is the doInBackground() code, runs in the background
    for (i : 1..3) { // number ranges, nice!
        runOnUiThread [ myButton.pressed = true ]
        Thread.sleep(250) // look ma! no try/catch!
        runOnUiThread [ myButton.pressed = false ]
    return true
].then [result|
    // This is the onPostExecute() code, runs on UI thread
    if (result) {
].onError [error|
    toast("Oops! " + error.message)

Note: sneaky throws, smoother error handling. See documentation for the many other benefits to using the AsyncBuilder.

Android boilerplate removal

Creating a Parcelable in Android:

public class Student implements Parcelable {
    private String id;
    private String name;
    private String grade;
    // Constructor
    public Student(){
    // Getter and setter methods
    // ... ommitted for brevity!
    // Parcelling part
    public Student(Parcel in){
        String[] data = new String[3];
        in.readStringArray(data); = data[0]; = data[1];
        this.grade = data[2];
    public int describeContents(){
        return 0;
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeStringArray(new String[] {,
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        public Student createFromParcel(Parcel in) {
            return new Student(in); 
        public Student[] newArray(int size) {
            return new Student[size];


// @Accessors creates getters/setters, @AndroidParcelable makes it parcelable!
@Accessors @AndroidParcelable class Student {
    String id
    String name
    String grade

Note: the above Xtendroid code essentially generates the same Android code above, into the build/generated folder, which gets compiled normally. Full bi-directional interoperability with existing Java classes.

Functional programming

@Accessors class User {
    String username
    long salary
    int age
var List<User> users = getAllUsers() // from somewhere...
var result = users.filter[ age >= 40 ].maxBy[ salary ]
toast('''Top over 40 is «result.username» earning «result.salary»'''

Note: String templating, many built-in list comprehension functions, lambdas taking a single object parameter implicitly puts in scope.

Builder pattern

// Sample Builder class to create UI widgets, like Kotlin's Anko
class UiBuilder {
   def static LinearLayout linearLayout(Context it, (LinearLayout)=>void initializer) {
      new LinearLayout(it) => initializer
   def static Button button(Context it, (Button)=>void initializer) {
      new Button(it) => initializer
// Now let's use it!
import static extension org.xtendroid.utils.AlertUtils.*
import static extension UiBuilder.*
contentView = linearLayout [
   gravity = Gravity.CENTER
   addView( button [
      text = "Say Hello!"
      onClickListener = [ 
         toast("Hello Android from Xtendroid!")

Note: You can create your own project-specific DSL!


import static extension org.xtendroid.utils.AlertUtils.*
import static extension org.xtendroid.utils.TimeUtils.*
var Date yesterday = 24.hours.ago
var Date tomorrow = 24.hours.fromNow
var Date futureDate = now + 48.days + 20.hours + 2.seconds
if (futureDate - now < 24.hours) {
    confirm("Less than a day to go! Do it now?") [
        // user wants to do it now

Note: using all of the above makes writing unit tests and instrumentation tests very easy, and fun!

未经允许不得转载:SEARU.ORG » Android特定语言 Xtendroid

赞 (0)
分享到:更多 ()

评论 0