XtendroidAndroid特定语言
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(R.id.my_button); 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()); results.put(result1); results.put(result2);
Xtendroid (Xtend) code:
var results = #[ #{ "result1" -> new JSONObject }, #{ "result2" -> new JSONObject } ]
Note: compact notation for Lists and Maps, method call brackets are optional
Multi-threading
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 ] Thread.sleep(250) } return true ].then [result| // This is the onPostExecute() code, runs on UI thread if (result) { toast("Done!") } ].onError [error| toast("Oops! " + error.message) ].start()
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); this.id = data[0]; this.name = data[1]; this.grade = data[2]; } @Оverride public int describeContents(){ return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeStringArray(new String[] {this.id, this.name, this.grade}); } 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]; } }; }
Xtendroid:
// @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!
Utilities
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 doIt() ] }
Note: using all of the above makes writing unit tests and instrumentation tests very easy, and fun!