Other links

Core design principle

"Being practically simple" is number one principle of Scaloid. Most frequently used things should be written shorter, like Huffman coding. To do this, I first observed Android programs I wrote, and thought that which part of the code is more fundamental than others. For example, what is the most essential part of buttons? Buttons should have some visible things on it, such as title or image, so the buttons are created like this: SButton("Hello"). The second essential part is doing something when it is pressed: SImageButton(R.drawable.hello, toast("World!")). What should be the third one? The answer might not the same for every people, but I think that repetition frequency of press-and-hold action is nice: SButton("Add", n += 1, 500) increases n for every 500 milliseconds when the user holds the button.

UI Layout without XML

Android SDK leverages XML to build UI layouts. However, XML is considered still a bit verbose, and lacks programmability. Scaloid composes UI layout in Scala DSL style, therefore achieve both clarity and programmability. For example, suppose a legacy XML layout as shown below:

Scaloid has highly flexible resource register/unregister management architecture.
If this code is written in services, registering and unregistering is done in onCreate and onDestroy respectively.
If the same code is in activities, registering and unregistering is done in onResume and onPause respectively.
This is just a default behavior.
You can override a preference that determine when the register/unregister preforms.
Overriding it is simple as well:

onDestroy is a method that adds a function into the job list triggered when the activity is destroyed. So, we just get a stream from openFileStream() and forget about releasing it.
Other lifecycle states (onCreate, onResume, onStop and so on) can be treated in the same way.

Running a job asynchronously and notifying the UI thread is a very frequently used pattern. Although Android API provides a helper class AsyncTask, implementing such a simple idea is still painful, even when we use Scala:

When you don't want to build sophisticate UI interactions, but just want to display something by calling a single Scaloid method (e.g. alert, toast, and spinnerDialog), Scaloid handles runOnUiThread for you. Therefore, the code block shown above is reduced to:

Future {
alert("Done!", doAJobTakeSomeTime(params))
}

It is a great win as it exposes your idea clearly.

Just like we thrown away AsyncTask, we can also eliminate all other Java helpers for asynchronous job, such as AsyncQueryHandler and AsyncTaskLoader. Compare with the original Java code
and a Scala port of ApiDemos example app.

Using Future is just an example of asynchronous task processing in Scaloid. You can freely use any modern task management utilities.

Further reading: Refer to this blog post for an important consideration when using Future in Android.

Implicit conversions

Scaloid employs several implicit conversions. Some of the available implicit conversions are shown below:

Uri conversion

String=>Uri

The functions such as play ringtonesplay() or open URIsopenUri() takes an instance of Uri as a parameter. However, we frequently have URIs as a String. Scaloid implicitly converts String into Uri. Therefore, you can freely use String when you play a ringtone:

Context as an implicit parameter

Many methods in the Android API require an instance of a class Context. Providing this for every method call results in clumsy code. We employ an implicit parameter to eliminate this. Just declare an implicit value that represents current context:

implicitvalctx= ...

or just extend trait SContext, which defines it for you. Then the code that required Context becomes much simpler, for example:

Intent

newIntent(context, classOf[MyActivity])

is reduced to:

SIntent[MyActivity]

When a method takes an Intent as a first parameter in which we want to pass the newly created intent object, the parameter can be omitted. For example:

All the system service accessors available in Android API level 8 are defined (e.g. audioManager, alarmManager, notificationManager, etc.). The name of a system service accessor is the same as its class name, except that the first character is lowercased.

Enriched Implicit classes

Suppose an Android class Foo, for example, Scaloid defines an implicit conversion Foo => RichFoo. The class RichFoo defines additional methods for more convenient access to Foo. This is a common pattern in Scala to extend existing API (see pimp-my-library pattern). This section describes various features added on existing Android API classes.

Listeners

Android API defines many listener interfaces for callback notifications. For example, View.OnClickListener is used to be notified when a view is clicked:

Scaloid provides a shortcut that dramatically reduces the length of the code:

find[Button](R.id.search).onClick(openUri("http://scaloid.org"))

All other listener-appending methods such as .onKey(), .onLongClick(), and .onTouch() are defined.

Some conventions we employed for method naming are:

We omit set..., add..., and ...Listener from the method name, which is less significant.
For example, .setOnKeyListener() becomes .onKey().

Every method has two versions of parameters overridden. One is a lazy parameter, and another is a function which has full parameters defined in the original Android API. For example, these two usages are valid:

Methods add... is abbreviated with a method += if it is not a listener-appender.
For example, layout.addView(button) becomes layout += button.

Multiple method listeners

Methods beforeTextChanged(), onTextChanged(), and afterTextChanged() are defined in RichTextView, which can be implicitly converted from TextView. It is more convenient than using TextWatcher directly. For example:

Layout context

In Android API, layout information is stored into a View object via the method View.setLayoutParams(ViewGroup.LayoutParams). A specific type of parameter passing into that method is determined by a the type of ...Layout object which contains the View object. For example, let us see some Java code shown below:

Because the button is appended into the LinearLayout, the layout parameter must be LinearLayout.LayoutParams, otherwise a runtime error might be occurred. Meanwhile, Scaloid eliminate this burden, while still preserving rigorous typing of LayoutParams. The code shown below is equivalent to the previous Java code:

vallayout=newSLinearLayout {
SButton("Click").<<.Weight(1.0f).>>
}

In the anonymous constructor of 'SLinearLayout', Scaloid provides an implicit function called "layout context". This affects a return type of the method << defined in the class SButton.
If we use SFrameLayout as a layout context, the method << returns FrameLayout.LayoutParams, which does not have Weight method. Therefore, the code below results a syntax error.

Prefixed classes in Scaloid (e.g. SButton) have a companion object that implements apply methods that create a new component. These methods also append the component to the layout context that enclose the component.
Therefore, the code block from the above example:

button =newSButton() text "Click"
button.here

is equivalent to:

button =SButton("Click")

Because the apply methods access to the layout context, it cannot be called outside of the layout context.
In this case, use the new operator instead.

Method >>

As we noted, the method << returns an object which is a type of ViewGroup.LayoutParams:

Methods fill, wrap, wf and fw

When we get a LayoutParams from <<, the default values of width and height properties are width = FILL_PARENT and height = WRAP_CONTENT. You can override this when you need it:

SButton("Click").<<(FILL_PARENT, FILL_PARENT)

This is a very frequently used idiom. Therefore we provide further shorthand:

SButton("Click").<<.fill

If you want the View element to be wrapped,

SButton("Click").<<(WRAP_CONTENT, WRAP_CONTENT)

This is also shortened as:

SButton("Click").<<.wrap

Similarly, <<(WRAP_CONTENT, FILL_PARENT) and <<(FILL_PARENT, WRAP_CONTENT) can also be shortend as <<.wf and <<.fw respectively.

Because there are so many occurences <<.wrap.>> pattern in actual Android code, it is allowed to remove .<< and .>> in this case:

SButton("Click").wrap // returns SButton type

This pattern also usable for .fill, .fw and .wf methods.

Styles for programmers

Naming conventions

Scaloid follows the naming conventions of XML attributes in the Android API with some improvements.

For XML attributes, layout related properties are prefixed with layout_ and as you might have guessed, Scaloid does not need it.
For boolean attributes, the default is false. However, Scaloid flags it as true when the attribute is declared explicitly without any parameter.
For example:

For layout methods named with four directions (e.g. ...Top, ...Right, ...Bottom and ...Left), Scaloid provides additional methods that specifies all properties at once. For example, Because Android XML layout defines margin... properties(marginTop(v:Int), marginRight(v:Int), marginBottom(v:Int) and marginLeft(v:Int)), Scaloid provides additional margin(top:Int, right:Int, bottom:Int, left:Int) and margin(amount:Int) methods that can be used as:

STextView("hello").<<.margin(5.dip, 10.dip, 5.dip, 10.dip)

or

STextView("hello").<<.margin(10.sp) // assigns the same value for all directions

Android SDK introduced styles to reuse common properties on XML layout.
We repeatedly pointed out that XML is verbose.
To apply styles in Scaloid, you do not need to learn any syntax or API library, because Scaloid layout is an ordinary Scala code. Just write a code that work as styles.

Advanced: CSS-like stylesheet

Scaloid provides SViewGroup.style(View => View) method to provide more generic component styling.
The parameter is a function which receives a view requested for styling, and returns a view which is finished applying the style.
Then the example in the previous subsection becomes:

Similar to CSS, you can assign different styles for each classes using Scala pattern matching.
Unlike Android XML styles or even CSS, Scaloid can assign some actions to the component (see onclick(toast(...))), or can do anything that you imagine.
Also, you can easily override the property individually, as shown in the example above.

Last thing that you may missed: These are type-safe. If you made a mistake, compiler will check it for you.

Traits

Trait UnregisterReceiver

When you register BroadcastReceiver with Context.registerReceiver() you have to unregister it to prevent memory leak. Trait UnregisterReceiver handles these chores for you. All you need to do is append the trait to your class.

Activity as an implicit parameter

Similar to the implicit context, an Activity typed implicit parameter is also required for some methods. Therefore, you have to define an activity as an implicit value:

implicitvalctx:Activity= ...

Because the class Activity is a subclass of Context, it can also be an implicit context.
When you extend SActivity, object this is assigned as the implicit activity by default.

Here we show some example cases of using the implicit activity:

Automatically allocate a unique View ID

Often, Views are required to have an ID value. Although Android API document specifies that the ID need not be unique, allocating unique ID is virtually mandatory in practice. Scaloid provides a package scope function getUniqueId, which returns Int type ID that is not allocated by any existing View components for given implicit activity.

valnewUniqueIdForCurrentActivity= getUniqueId

Using this, Scaloid also extended View class to add a method uniqueId, that assigns a new unique ID if it is not already allocated.

valuniqueIdOfMyView= myView.uniqueId

One of the good use case of uniqueId is SRelativeLayout. Some of the methods in this layout context, such as below, above, leftOf and rightOf, takes another View object as an anchor:

A new unique ID is assigned to the anchor if it is not assigned already, and passes it to addRule function.

Logging

Unlike other logging frameworks, Android Logging API requires a String tag for every log call. We eliminate this by introducing an implicit parameter. Define an implicit value type of LoggerTag as shown:

implicitvalloggerTag=LoggerTag("MyAppTag")

or, extend trait TagUtil or SContext which defines the tag by default. Then you can simply log like this:

warn("Something happened!")

Other functions for every log level (verbose(), debug(), info(), warn(), error() and wtf()) are available.

info("hello "+ world)

A String parameter passed with info() is a by-name parameter, so it is evaluated only if the logging is possible. Therefore, the example shown above is equivalent to:

that of Scala style clearly reveals the nature of the operations as shown below:

newSTextView {
text ="Hello"
textSize =15
}

Or, you can also chain the setters:

newSTextView text "Hello" textSize 15

which is a syntactic sugar for:

newSTextView.text("Hello").textSize(15)

We recommend "assignment style" and "DSL style". Use assignment style when you emphasize that you are assigning something, or use DSL style when the code length of the assignee is short and needs to be chained.

Note: Using .apply(String) method on object STextView, you can further reduce the code above like this:

If you want to let the text color in the spinner be blue, use the style method:

SArrayAdapter("Quick", "Brown", "Fox").style(_.textColor(Color.BLUE))

Can it be simpler?

Class LocalService

Android Developer Guide on service binding says that we have to write more than 60 lines of code to define and bind an in-process service.
With Scaloid, you can concisely define and access local service as shown below:

Extending View class

Often we need to define a custom view widget for a specific requirement.
To do this, we define a class that inherits android.widget.View class or its subclass (e.g. TextView and Button).
To enable Scaloid extensions for this custom widget, you can define a class as follows:

We are hiring!

The company behind Scaloid, onsquare is hiring Scala developers.
We are building a music app and other amazing products.
We extensively uses Scaloid in our product, and probably it is the best reference of Scaloid application.
For more information about our company, please refer to our website http://o-n2.com .
Please send us your CV via email if you are interested in working at onsqure.
We are located at Incheon, Korea.
pocorall@gmail.com