Today we look in more detail at the scala-android.jar
library we mentioned at the end of our article
"Translating Java code to Scala".
At this stage of its development that library provides basic functionality
but should support more advanced features in a next future as does the
scala-swing.jar
library (part of the standard
Scala software distribution).
scala-android.jar
The scala-android.jar
library exists in two slightly different
versions: the android-8
version is actually a superset of
android-7
version and provides a few API additions (mostly
constants) introduced in release 2.2 of the Android platform.
android-examples> tree scala-framework/android/src/
scala-framework/android/src/
|-- android-7
| `-- scala
| `-- android
| |-- app
| | |-- Activity$.java
| | `-- Activity.scala
| |-- maps
| | |-- Overlay$.java
| | `-- Overlay.scala
| |-- package.scala
| `-- provider
| |-- Contacts.java
| `-- ContactsContract.java
`-- android-8
`-- <same hierarchy as in "android-7">
12 directories, 14 files
Activity
class declares convience methods such
as findButton
, findTextView
, etc. in addition
to the findViewById
method and implicit conversions for
coding common callback methods such as onClick
,
onCheckedChanged
, etc. in a concise way (see example 2 below).
Overlay
class declares one single compatibility method,
drawAt
, which provides a bridge to the corresponding
(yet unaccessible) Java method of the Google Maps API (see article
"Translating Java code to Scala").
Contacts
is provided for convenience
only and both Java and Scala programmers are strongly encouraged to use
the ContactsContract
class instead.
android
— defined
in the file android/package.scala
by convention —
declares convenience methods for helping the programmer to write more
concise code.
Wrapper objects such as scala.android.provider.ContactsContract
give direct access to inherited Java constants defined in Java static inner
interfaces. For instance, in the following Scala code we access the
constant Phone.TYPE
directly as in Java although it is actually
defined in the static inner interface CommonColumns
(see the
blog entry "Scala on Android Gotcha: HelloGridView Error" for more details).
// ContactAdder.java import android.provider.ContactsContract.CommonDataKinds.Phone; //.. ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE) .withValue(Phone.NUMBER, phone) .withValue(Phone.TYPE, phoneType) .build());
// ContactAdder.scala import scala.android.provider.ContactsContract.CommonDataKinds.Phone //.. ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE) .withValue(Phone.NUMBER, phone) .withValue(Phone.TYPE, phoneType) .build())
Boilerplate code can be reduced using Scala implicit conversions. Thus
passing event handlers to methods setOnItemSelectedListener
and setOnClickListener
can be expressed in a more concise way
by the Scala programmer (see the StackOverflow blog entry "What is in scala-android.jar ?"
for an example).
// ContactAdder.java import android.app.Activity; //.. mAccountSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView<?> parent, View view, int position, long i) { updateAccountSelection(); } public void onNothingSelected(AdapterView<?> parent) { // We don't need to worry about nothing being selected, // since Spinners don't allow this. } }); mContactSaveButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { onSaveButtonClicked(); } }); private void onSaveButtonClicked() { /* .. */ }
// ContactAdder.scala import scala.android.app.Activity //.. mAccountSpinner setOnItemSelectedListener { updateAccountSelection() } mContactSaveButton setOnClickListener { onSaveButtonClicked() }
Note: In case the Scala programmer needs to provide some code for the
onNothingSelected
event he can simply write:mAccountSpinner setOnItemSelectedListener { updateAccountSelection() } orNothing { // onNothingSelected code }
to be done