FacebookTwitterGoogle+Share

haxe to java for android (part 2)

Last time I discussed setting up a project to create java code, for android, from haxe. I left out some neat stuff because it was already pretty long. If you read it, you probably noticed.

For example, the program was called Haxe-Java Toast, which from the code presented is wildly inaccurate. Well, maybe not wildly, but you get the idea.

My goal was to do was create an android application with its interface defined by XML, and which called a function from the Android SDK (in this case triggered a Toast). What I pasted last time was a subset of the code in MyActivity.java.

There may end up being a lot of code here.

package com.gigglingcorpse.test.haxejavatoast;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.widget.EditText;
class MyActivity extends Activity {
	public var btnToast:Button;
	public var editBox:EditText;
	override public function onCreate( state:Bundle ) {
		super.onCreate(state);
		Log.i("trace", "starting android haxe-java toast");      
		setContentView(r.Layout.main);
		btnToast = cast( findViewById(r.Id.btnToast), Button );
		editBox = cast( findViewById(r.Id.message), EditText );
	}
	public function sendToast( view:View ) {
		var s:String = editBox.getText().toString();
		if ( s.length == 0 )
			s = "You've typed nothing!";
		var t = Toast.makeText( this, s, Toast.LENGTH_LONG);
		t.show();
	}	
}

I defined the files for android classes that I wanted to import. They are externs, and contain only the bare minimum that I required.

In some cases, they are outright lies.

(I have defined android.app.Activity to extend Context. In fact, Activity extends ContextThemeWrapper.. and so it continues down until it eventually gets to Context.

I needed to pass it as a Context (and am lazy), so that was that.)

In the most complicated of the extern classes I’ve defined a few functions and static fields.

package android.widget;
import android.content.Context;
extern class Toast {
	public static function makeText( context:Context, message:String, duration:Int ) : Toast ;
	public function show() : Void;
	public static var LENGTH_LONG : Int;
	public static var LENGTH_SHORT : Int;
}

In most cases I’ve left their bodies entirely empty.

package android.widget;
extern class Button {}

In the future, I am confident that these classes will be automatically generated, and available for easy inclusion and use. At the moment, they are not.


Res/ and R.

I mentioned the res directory earlier.

The res directory is pretty special. It usually contains resources for your APK.

Here, it just contains the application’s layout. In assets/res/layout I created a file called main.xml.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
	<RelativeLayout android:id="@+id/RelativeLayout01" 
		android:layout_width="fill_parent" 
		android:layout_height="wrap_content">
			<EditText android:id="@+id/message" 
				android:layout_height="wrap_content" 
				android:layout_width="fill_parent"  
				android:background="@android:drawable/editbox_background" />
	</RelativeLayout>
	<RelativeLayout android:id="@+id/RelativeLayout02" 
		android:layout_width="fill_parent" 
		android:layout_height="wrap_content">
			<Button android:id="@+id/btnToast" 
			android:layout_width="wrap_content" 
			android:layout_height="wrap_content" 
			android:text="Toast" 
			android:onClick="sendToast" />
	</RelativeLayout>
</LinearLayout>

The layout defines two objects: An EditText, and a Button.

To see if it would work, I defined an android:onClick property in the Button entry. This is how it knows which function to call when clicked. (Spoiler: it worked!)

 

The way android handles resource files is a little strange. You can access them through the R object.

This is how you would do it in Java.

setContentView( R.layout.main );
findViewById( R.id.btnToast );

The way I handled it for haxe is almost certainly not the best way to do so. But it works, is easy, and could be automated without too much effort.

If you compared the above Java and Haxe calls, you probably noticed the casing differences. This is because in Java R is a class. layout and id are nested classes.

In Haxe, I’ve made r a package, and and Layout and Id classes within it — using the @:native(…) tag to handle the case differences.

The contents of my java-src/r/Id.hx file are:

package r;
@:native("R.id") extern class Id {
	public static var btnToast:Int;
	public static var message:Int;
}

So r.Id.btnToast in Haxe gets converted to R.id.btnToast in Java.

THE END

 

Comments

  1. NME has a script to automatically generate haxe classes based on a compiled Java application. It’s meant to generate JNI bindings, for interfacing with them from C++ (in a traditional NME Java application) but it might be neat to apply the same principle to generate Java extern classes automatically.

    Once the Java target becomes “mainstream”, it would be great to explore a Java target for Android, where the higher-level code stays in Java, and uses JNI only to access the low-level framework calls in NME. Being able to spit out a bunch of extern classes automatically is much cleaner than using JNI bindings to interact with them 🙂

  2. Brad says:

    I really like everything you said there. So many neat possibilities! 😀

You must be logged in to post a comment.