Brian Long Consultancy & Training Services
Ltd.
January 2012
This article looks at how event handlers work in the Java world that Android inhabits, how it differs from the Delphi and .NET way that many Oxygene for Java users will be more familiar with, and how the new inline interface implementation feature of Oxygene 5 helps work with Java-style events.
If you are new to Oxygene for Java, follow this link to an introductory article on using it to create Android applications.
The accompanying source project is available through this download link.
In Delphi we are familiar with events being special extensions to method pointers,
defined as procedural types using the of object
suffix, i.e. closures.
When you define a data field using one of these types and then (typically) expose
it from the class using a property definition, you're all set. Any object
instance's method with a
suitable signature can be assigned to this Delphi single-casting event field/property.
In .NET things are not that different. At the implementation level you're creating
a descendant of a System.MulticastDelegate
object, but in code you
just add in the delegate
keyword to what is essentially a procedural
type definition and you get the required effect. Ignoring the specifics of how you
add and remove handlers to a .NET multicast delegate we are essentially dealing,
as in Delphi, with a field defined in terms of a procedural type definition.
In Java things are rather different. The standard procedure in the Java world is to define events in terms of interfaces, typically interfaces with a single method declared in them. This interface method dictates the signature of a suitable event handler, but of course you then need to have a class at your disposal that implements the interface and which you can assign to an event handler data field, whose type will be the interface type.
This is a bit of a switch around from what we may be familiar with in the world of Delphi and .NET. In those environments you just need an object (any object, including a form or whatever) to define a method with a matching signature and you can then add a reference to the method (from an appropriate instance of the object) to the event field/property.
The following sections show various options available to the Oxygene for Java programmer for handling an event defined using an interface type. Hopefully this will help smooth the transition for Delphi programmers looking at writing Android applications in Oxygene for Java.
The scenario covered by each option is a Button
widget displayed by the Activity
(aka screen) whose click event we want
to handle. A click event for any view is defined in terms of the View.OnClickListener
interface, which has a single
method, onClick
, taking a View
parameter. It is usually set up with the
view's setOnClickListener()
method (or optionally, in Oxygene,
the OnClickListener
write-only implied property).
Bearing in mind that an event is defined in terms of an interface type, the first option
we'll consider is where we implement the View.OnClickListener
event
interface in the button's activity class, as shown here:
The handler is set up by assigning the activity object, self
, to
OnClickListener
.
While this approach works perfectly fine, there's a glaring shortcoming with it. Having implemented the interface method once for this one button that's about all we can do. If we want respond to the user clicking any other button or any other control in this activity we can't use the same approach for them as we've already implemented the click listener interface and given its method some functionality. So while it works as a one-off, it's not really that useful in the general case, at least not for such a general purpose event as the click event. There might be an argument for its use in the case of a very specific event of a widget of which there is a single instance. And certainly in the general non-event case of needing to implement an interface it's all fine, but for event handling, it's really of limited use.
Of course you could also declare additional class types in order to implement the interface more times for other handlers but that quickly becomes unwieldy. So, ignoring that possibility, what's next?
Now we come to this new Oxygene syntax, added to the language to allow Oxygene programmers to work with these interface-based events in much the same way as Java programmers do. The idea is to have the compiler generate an anonymous class that implements the event interface. Then for each method defined in the interface you can (optionally) provide a method with a suitable signature that will be used as the implementation of the interface's method.
Let's have a look at how the syntax of inline interface implementation is laid out:
You can see the new interface
construct being used to get the compiler
to create an anonymous object that implements the View.OnClickListener
interface, and then the bracketed section sets up the interface methods. Given that
an interface can define multiple methods we have a comma-separated list here (if
necessary - not in this case) that associates
a method of the interface with a method to implement it with, which of course requires
the appropriate matching signature. In the case of passing an existing method, in this case
one in this activity class, we require an @
prefix. For any interface
methods that we are not interested implementing we can just ignore them; the compiler
will supply an empty stub method. In this case that latter point is irrelevant as
the interface in question only has one method, onClick
.
This next possible approach is exactly the same as Option 2 above, just slightly abbreviated. In cases where the interface does have just a single method we are permitted a small abbreviation, leaving an assignment which is quite reminiscent of an old Delphi event property assignment.
Again, this is specific to interfaces defined to contain just a single method.
The next possibility is to use a variant on Option 2 above. Instead of using an existing method with the required signature, we can define an anonymous method in situ:
This is useful if the code is quite brief and is only needed for this particular
use. It does, however, offer the benefit that it has access to the locals of the subroutine
it is implemented in. So if we had useful local variables defined in onCreate()
then the anonymous method's implementation could access them.
Rather similar to Option 4 above is the option to use a lambda expression instead of anonymous method.
In this scenario a lambda expression is basically a shortcut for writing an anonymous method and so again, the body of the lambda has access to any locals defined in the enclosing method.
All the previous examples show different ways to set up an event handler for (in this example's case) a button click event. This final option uses no Oxygene code at all to set up the handler and so doesn't even need a variable to represent the button:
This code-less approach is achieved by setting up the event handler iun the activity's layout file:
Buttons can have an onClick
attribute in the XML layout to identify the
click event handler
method in the activity class their layout gets loaded into. Other views can also use
this onClick
attribute, however whether they respond to clicks by calling
the specified handler or not is dependant on the value of their clickable
attribute (or whether setClickable(true)
or Clickable := true
has been executed). The Button
class
sets clickable
to true
.
Event handling is different in the Java world to what we might be familiar with from experience in the Delphi or .NET world - events are managed through interface references. Oxygene for Java takes account of this alternate approach and offers up a new syntax and a variety of options to flexibly work with events in the appropriate Java-esque way. Here we've looked at a handful of possibilities to ensure you are up to date on what options are available and so that sample code is more digestible to you when you encounter it.
If you wish to make any comments on this article, your best options are to comment on the blog post that announced it, use the Contact Me form or email me at .
Brian Long has spent the last 1.6 decades as a trainer, trouble-shooter and mentor focusing on the Delphi, Oxygene, C# and C++ languages, and the Win32, .NET and Mono platforms, recently adding iOS and Android onto the list. In his spare time, when not exploring the Chiltern Hills on his mountain-bike or pounding the pavement in his running shoes, Brian has been re-discovering and re-enjoying the idiosyncrasies and peccadilloes of Unix-based operating systems. Besides writing a Pascal problem-solving book in the mid-90s he has contributed chapters to several books, written countless magazine articles, spoken at many international developer conferences and acted as occasional Technical Editor for Sybex. Brian has a number of online articles that can be found at http://blong.com and a blog at http://blog.blong.com.
© 2012 Brian Long Consulting and Training Services Ltd. All Rights Reserved.
Go back to the top of this page