Scaloid makes your Android code easy to understand and maintain.

Overview

Simpler Android

Scaloid is a library that simplifies your Android code. It makes your code easy to understand and maintain by leveraging Scala language.

For example, the code block shown below:

val button = new Button(context)
button.setText("Greet")
button.setOnClickListener(new OnClickListener() {
  def onClick(v: View) {
    Toast.makeText(context, "Hello!", Toast.LENGTH_SHORT).show()
  }
})
layout.addView(button)

is reduced to:

SButton("Greet", toast("Hello!"))

Benefits

Demos

Fork one of this to start a new project:

Learn how Scaloid can be used in action:

Contents

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:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="wrap_content" android:padding="20dip">
    <TextView android:layout_width="match_parent"
            android:layout_height="wrap_content" android:text="Sign in"
            android:layout_marginBottom="25dip" android:textSize="24.5sp"/>
    <TextView android:layout_width="match_parent"
            android:layout_height="wrap_content" android:text="ID"/>
    <EditText android:layout_width="match_parent"
            android:layout_height="wrap_content" android:id="@+id/userId"/>
    <TextView android:layout_width="match_parent"
            android:layout_height="wrap_content" android:text="Password"/>
    <EditText android:layout_width="match_parent"
            android:layout_height="wrap_content" android:id="@+id/password"
            android:inputType="textPassword"/>
    <Button android:layout_width="match_parent"
            android:layout_height="wrap_content" android:id="@+id/signin"
            android:text="Sign in"/>
    <LinearLayout android:orientation="horizontal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
        <Button android:text="Help" android:id="@+id/help"
                android:layout_width="match_parent" android:layout_height="wrap_content"/>
        <Button android:text="Sign up" android:id="@+id/signup"
                android:layout_width="match_parent" android:layout_height="wrap_content"/>
    </LinearLayout>
</LinearLayout>

is reduced to:

new SVerticalLayout {
  STextView("Sign in").textSize(24.5.sp).<<.marginBottom(25.dip).>>
  STextView("ID")
  SEditText()
  STextView("Password")
  SEditText() inputType TEXT_PASSWORD
  SButton("Sign in")
  new SLinearLayout {
    SButton("Help")
    SButton("Sign up")
  }.wrap.here
}.padding(20.dip)

The layout description shown above is highly programmable. You can easily wire your logic into the layout:

new SVerticalLayout {
  STextView("Sign in").textSize(24.5.sp).<<.marginBottom(25.dip).>>
  STextView("ID")
  val userId = SEditText()
  STextView("Password")
  val pass = SEditText() inputType TEXT_PASSWORD
  SButton("Sign in", signin(userId.text, pass.text))
  new SLinearLayout {
    SButton("Help", openUri("http://help.url"))
    SButton("Sign up", openUri("http://signup.uri"))
  }.wrap.here
}.padding(20.dip)

Because a Scaloid layout description is plain Scala code, it is type-safe.

Automatic layout converter

This converter turns an Android XML layout into a Scaloid layout:

http://layout.scaloid.org

Migration tip

Scaloid is fully compatible with legacy xml layout files. You can access a widget described in xml layout as:

onCreate {
  setContentView(R.layout.main)
  val name = find[EditText](R.id.name)
  // do something with `name`
}

Responsive layout

Basically, a layout written in Scaloid is just an ordinary Scala code, so you can just freely composite the layout according to the device configuration:

import org.scaloid.util.Configuration._

if(long) SButton("This button is shown only for a long screen "
  + "dimension ("+ width + ", " + height + ")")
if(landscape) new SLinearLayout {
  SButton("Buttons for")
  SButton("landscape layout")
  if(dpi <= HDPI) SButton("You have a high resolution display!")
}.here

Please refer to this blog post for more detail:

Further readings about Scaloid layout

Lifecycle management

With Android API, Registering and unregistering BroadcastReceiver can be done as:

var connectivityListener: BroadcastReceiver = null

def onResume() {
  super.onResume()
  // ...
  connectivityListener = new BroadcastReceiver {
    def onReceive(context: Context, intent: Intent) {
     doSomething()
    }
  } 
  registerReceiver(connectivityListener, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
}

def onPause() {
  unregisterReceiver(connectivityListener)
  // ...
  super.onPause()
}

In Scaloid, the directly equivalent code is:

broadcastReceiver(ConnectivityManager.CONNECTIVITY_ACTION) {
  doSomething()
}

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:

broadcastReceiver(ConnectivityManager.CONNECTIVITY_ACTION) {
  doSomething()
}(this, onStartStop)

Then, the receiver is registered onStart, and unregistered onStop.

onDestroy can be called in many times!

You can declare onDestroy behaviors in many places. This simplifies resource management significantly. Suppose you want to open a stream from a file:

def openFileStream(file: File): InputStream = {
  val stream = new FileInputStream(file)
  onDestroy(stream.close()) // automatically closed when the Activity is destroyed!!
  stream
}

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.

Further reading: Refer to this blog post for more details.

Asynchronous task processing

Android API provides runOnUiThread() only for class Activity. Scaloid provides a Scala version of runOnUiThread() for anywhere other than Activity.

Instead of:

activity.runOnUiThread {
  new Runnable() {
    def run() {
      debug("Running only in Activity class")
    }
  }
}

In Scaloid, use it like this:

runOnUiThread(debug("Running in any context"))

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:

new AsyncTask[String, Void, String] {
  def doInBackground(params: Array[String]) = {
    doAJobTakeSomeTime(params)
  }

  override def onPostExecute(result: String) {
    alert("Done!", result)
  }
}.execute("param")

Using scala.concurrent.Future, the asynchronous job shown above can be rewritten like this:

Future {
  val result = doAJobTakeSomeTime(params)
  runOnUiThread(alert("Done!", result))
}

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 ringtones play() or open URIs openUri() 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:

play("content://media/internal/audio/media/50")

, open a URI:

openUri("http://scaloid.org")

, or wherever you want.

Alternatively, you can specify the conversion as:

val uri:Uri = "http://scaloid.org".toUri
Unit conversion

Units dip and sp can be converted into the pixel unit.

val inPixel:Int = 32.dip
val inPixel2:Int = 22.sp

Reversely, pixel unit can also be converted into dip and sp unit.

val inDip:Double = 35.px2dip
val inSp:Double = 27.px2sp
Resource IDs

Scaloid provides several implicit conversions that convert from Int type resource ID to CharSequence, Array[CharSequence], Array[String], Drawable and Movie. For example:

def toast(msg:CharSequence) = ...

toast(R.string.my_message) // implicit conversion works!

Although Scaloid provides these conversions implicitly, explicit conversion may be required in some context. In this case, methods r2... are provided for the Int type:

warn("Will display the content of the resource: " + R.string.my_message.r2String)

Currently, r2Text, r2TextArray, r2String, r2StringArray, r2Drawable and r2Movie is provided.

Further reading:

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:

implicit val ctx = ...

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

Intent
new Intent(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:

startService(new Intent(context, classOf[MyService]))
stopService(new Intent(context, classOf[MyService]))

is reduced to:

startService[MyService]
stopService[MyService]

or

val intent = // initialize the intent and put some attributes on it
intent.start[MyActivity]

An intent that has a long list of extra attributes:

new Intent().putExtra("valueA", valueA).putExtra("valueB", valueB).putExtra("valueC", valueC)

is reduced to:

new Intent().put(valueA, valueB, valueC)
Toast
toast("hi, there!")

If you want a longer toast:

longToast("long toast")
Dialog
ProgressDialog.show(context, "Dialog", "working...", true)

is reduced to:

spinnerDialog("Dialog", "working...")

When you call toast, longToast or spinnerDialog from non-UI thread, you don't have to mind about threading. The toast example shown above is equivalent to the following Java code:

activity.runOnUiThread(new Runnable() {
    public void run() {
        Toast.makeText(activity, "hi, there!", Toast.LENGTH_SHORT).show();
    }
});
Pending intent
PendingIntent.getActivity(context, 0, new Intent(context, classOf[MyActivity]), 0)
PendingIntent.getService(context, 0, new Intent(context, classOf[MyService]), 0)

is reduced to:

pendingActivity[MyActivity]
pendingService[MyService]
Open URIs

This opens a web browser (or another view assigned to the http protocol).

openUri("http://scaloid.org")
System services

Getting system service objects become much simpler. The following legacy code:

val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE).asInstanceOf[Vibrator]
vibrator.vibrate(500)

is reduced to:

vibrator.vibrate(500)

Under the hood, Scaloid defines a function vibrator like this:

def vibrator(implicit ctx: Context) = ctx.getSystemService(Context.VIBRATOR_SERVICE).asInstanceOf[Vibrator]

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:

find[Button](R.id.search).setOnClickListener(new View.OnClickListener {
  def onClick(v:View) {
    openUri("http://scaloid.org")
  }
})

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:
button.onClick(info("touched"))
button.onClick((v:View) => info("touched a button "+v))
  • 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:

inputField.beforeTextChanged(saveTextStatus())

is equivalent to:

inputField.addTextChangedListener(new TextWatcher {
  def beforeTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
    saveTextStatus()
  }

  def onTextChanged(p1: CharSequence, p2: Int, p3: Int, p4: Int) {}

  def afterTextChanged(p1: Editable) {}
})

Also, we override beforeTextChanged() with full parameters defined in the original listener:

inputField.beforeTextChanged((s:CharSequence, _:Int, _:Int) => saveText(s))

Other listeners in Android API can also be accessed in this way.

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:

LinearLayout layout = new LinearLayout(context);
Button button = new Button(context);
button.setText("Click");
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams();
params.weight = 1.0f;  // sets some value
button.setLayoutParams(params);
layout.addView(button);

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:

val layout = new SLinearLayout {
  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.

val layout = new SFrameLayout {
  SButton("Click").<<.Weight(1.0f).>>   // Syntax error on Weight()
}

Compared with XML layout description, Scaloid layout is simple and type-safe.

The method << is overloaded with parameters <<(width:Int, height:Int) which assigns the size of the view component. For example:

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

Operator new and method apply

Usually, View components are referenced multiple times in an Activity. For example:

lazy val button = new SButton() text "Click"
onCreate {
  contentView = new SLinearLayout {
    button.here
  }
}
// ... uses the button somewhere in other methods (e.g. changing text or adding listeners)

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 = new SButton() 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:

val params = SButton("Click").<<   // type LayoutParams

This class provides some setters for chaining:

val params = SButton("Click").<<.marginBottom(100).marginLeft(10)   // type LayoutParams

if we want use the SButton object again, Scaloid provides >> method returning back to the object:

val button = SButton("Click").<<.marginBottom(100).marginLeft(10).>>   // type SButton

Nested layout context

When the layout context is nested, inner-most layout's context is applied:

val layout = new SFrameLayout {
  new SLinearLayout {
    SButton("Click").<<.Weight(1.0f).>>   // in context of SLinearLayout
  }.here
}

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:

new SRelativeLayout {
  STextView("hello").<<.centerHorizontal.alignParentBottom.>>
}

Scaloid omits unnecessary ="true" for the attribute centerHorizontal. Equivalent XML layout description for TextView is:

<TextView
    android:id="@+id/helloText"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_alignParentBottom="true"
    android:text="hello"/>

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.

Basic: Assign it individually

Suppose the following code that repeats some properties:

SButton("first").textSize(20.dip).<<.margin(5.dip).>>
SButton("prev").textSize(20.dip).<<.margin(5.dip).>>
SButton("next").textSize(20.dip).<<.margin(5.dip).>>
SButton("last").textSize(20.dip).<<.margin(5.dip).>>

Then we can define a function that applies these properties:

def myStyle = (_: SButton).textSize(20.dip).<<.margin(5.dip).>>
myStyle(SButton("first"))
myStyle(SButton("prev"))
myStyle(SButton("next"))
myStyle(SButton("last"))

Still not satisfying? Here we have a shorter one:

def myStyle = (_: SButton).textSize(20.dip).<<.margin(5.dip).>>
List("first", "prev", "next", "last").foreach(title => myStyle(SButton(title)))

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:

style {
  case b: SButton => b.textSize(20.dip).<<.margin(5.dip).>>
}

SButton("first")
SButton("prev")
SButton("next")
SButton("last")

Note that individually applying myStyle is reduced. Let us see another example:

style {
  case b: SButton => b.textColor(Color.RED).onClick(toast("Bang!"))
  case t: STextView => t.textSize(10.dip)
  case v => v.backgroundColor(Color.YELLOW)
}

STextView("I am 10.dip tall")
STextView("Me too")
STextView("I am taller than you").textSize(15.dip) // overriding
SEditText("Yellow input field")
SButton("Red alert!")

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.

Further readings:

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.

class MyService extends SService with UnregisterReceiver {
  def func() {
    // ...
    registerReceiver(receiver, intentFilter)
    // Done! automatically unregistered at UnregisterReceiverService.onDestroy()
  }
}

Trait SActivity

Instead of

findViewById(R.id.login).asInstanceOf[Button]

use a shorthand:

find[Button](R.id.login)

Although we provide this shorthand, Scaloid recommends programmatically laying out UI, not with XML.

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:

implicit val ctx: 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.

val newUniqueIdForCurrentActivity = 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.

val uniqueIdOfMyView = 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:

new SRelativeLayout {
  val btn = SButton(R.string.hi)
  SButton("There").<<.below(btn)
}

Here we show the implementation of the below function:

def below(anchor: View)(implicit activity: Activity) = {
  addRule(RelativeLayout.BELOW, anchor.uniqueId)
  this
}

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:

implicit val loggerTag = 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:

val tag = "MyAppTag"
if(Log.isLoggable(tag, Log.INFO)) Log.i(tag, "hello " + world)

Scala getters and setters

You can use any of the setters listed below:

  • obj.setText("Hello") Java bean style
  • obj.text = "Hello" Assignment style
  • obj text "Hello" DSL style
  • obj.text("Hello") Method calling style

Compared to Java style getters and setters, for example:

new TextView(context) {
  setText("Hello")
  setTextSize(15)
}

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

new STextView {
  text = "Hello"
  textSize = 15
}

Or, you can also chain the setters:

new STextView text "Hello" textSize 15

which is a syntactic sugar for:

new STextView.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:

STextView("Hello") textSize 15

Further readings:

Classes

Class AlertDialogBuilder

A Scala-style builder for AlertDialog.

new AlertDialogBuilder(R.string.title, R.string.message) {
  neutralButton()
}.show()

This displays an alert dialog with given string resources. We provide an equivalent shortcut:

alert(R.string.title, R.string.message)

Also you can build a more complex dialog:

new AlertDialogBuilder("Exit the app", "Do you really want to exit?") {
  positiveButton("Exit", finishTheApplication())
  negativeButton(android.R.string.cancel)
}.show()

The code above is equivalent to:

new AlertDialog.Builder(context)
  .setTitle("Exit the app")
  .setMessage("Do you really want to exit?")
  .setPositiveButton("Exit", new DialogInterface.OnClickListener {
    def onClick(dialog: DialogInterface, which: Int) {
      finishTheApplication()
    }
  })
  .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener {
    def onClick(dialog: DialogInterface, which: Int) {
      dialog.cancel()
    }
  }).show()

When you call show() or alert from non-UI thread, you don't have to mind about threading.

Class SArrayAdapter

Suppose you want to let the user selects a string from spinner, and larger font should be displayed in the dropdown list. Then the plain-old Android code is consisted of a chunk of XML and its wiring:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    style="?android:attr/spinnerDropDownItemStyle"
    android:id="@+id/spinner_textview"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
	android:textSize="25.dip" />
val adapter = new ArrayAdapter(context, android.R.layout.simple_spinner_item, Array("One", "Two", "Three"))
adapter.setDropDownViewResource(R.layout.spinner_dropdown)

In Scaloid, a directly equivalent code is:

SArrayAdapter("One", "Two", "Three").dropDownStyle(_.textSize(25.dip))

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:

class MyService extends LocalService {
  private val generator = new Random()

  def getRandomNumber() = generator.nextInt(100)
}

class Activity extends SActivity {
  val random = new LocalServiceConnection[MyService]
 
  def onButtonClick(v:View) {
    random( s => toast("number: " + s.getRandomNumber()))
  }
}

Further reading: Refer to this blog post to see why this is awesome in compared with the existing method.

Class Preferences

SharedPreference can be accessed in this way:

val executionCount = preferenceVar(0) // default value 0
val ec = executionCount() // read
executionCount() = ec + 1 // write
executionCount.remove() // remove

Further reading:

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:

class MyView(implicit ctx: Context) extends View(ctx) with TraitView[MyView] {
  def basis = this
  
  // custom code for MyView here
}

Let's make it together!

Scaloid is an Apache licensed project. If you have any idea to improve Scaloid, feel free to open issues or post patches. If you want look into inside of Scaloid, this document would be helpful:

List of projects using Scaloid

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. [email protected]

Comments
  • Group into separate packages

    Group into separate packages

    Hi,

    Thanks for these lovely helpers! I decided to use it for my first android project in Scala and looking into it.

    common.scala was small at first but now it has became a beast of 3,304 lines. Placing them into mimicked android package structure will make it easier to understand I guess.

    enhancement 
    opened by guersam 27
  • Add implicit conversions to Parcelable

    Add implicit conversions to Parcelable

    I frequently use Android's Parcelable interface to allow for my classes to be transported across Activity changes (e.g., screen rotations or passing via Intents). Android developers endorse Parcelable over Serializable due to its improved speed.

    The downside to Parcelable is that implementing it brings a ton of boilerplate. Here is a "small" example of a Parcelable class:

    import android.os.{Parcel, Parcelable}
    
    class Foo(val bar: String) extends Parcelable {
      override def describeContents: Int = 0
    
      override def writeToParcel(dest: Parcel, flags: Int): Unit = dest.writeString(bar)
    }
    
    object Foo {
      val CREATOR = new Parcelable.Creator[Foo] {
        override def createFromParcel(source: Parcel): Foo = new Foo(source.readString())
    
        override def newArray(size: Int): Array[Foo] = new Array(size)
      }
    }
    

    This seems like a task well-suited to Scala's implicits/traits. In particular, I was hoping for a solution that incorporates some of these ideas:

    1. Implicit conversions from Java's primitive types to Parcelable. Currently, Parcel has a bazillion different methods for reading/writing primitive types (e.g., readInt()/writeString(), readString()/writeString(), etc.) plus readParcelable()/writeParcelable(). I would like to only have to use the latter, so having implicit conversions from Int, String, etc. to Parcelable would be very useful.
    2. Parcelable conversions for commonly-used collections. For example, List[P], where P has an implicit conversion to Parcelable.
    3. Some sort of simpler trait to extend that allows a class to be Parcelable. (SParcelable?)

    The biggest obstacle that prevents me from doing this myself is Parcelable's unusual requirement that all classes implementing it must have a static field named CREATOR (there exists a Scala compiler hack that allows you to define CREATOR as a value in the class's companion object). There would need to be some way of retrofitting classes such as Int and String to have a static CREATOR field, and a way for classes which extend SParcelable to have a static CREATOR field.

    I'm not sure what the best way of approaching this is, so I'm open to suggestions. Scala macros are the only way that I can think of accomplishing this, but I'm not very well-versed in macros.

    opened by RyanGlScott 26
  • Too many methods when predexing Scaloid

    Too many methods when predexing Scaloid

    I use preloading a lot to reduce my Scala on Android build times. This basically means that I run my libraries (Scala, usually) through the dx utility, and then upload them to my device.

    I recently tried running the new Scaloid after a while using an old version, and, much to my dismay, the number of methods in the JAR is now a whooping 76243. Which is even bigger than scala-library and can't even be predexed! This is the output I get when I try :

    trouble writing output:
    com.android.dx.util.DexException: Too many methods: 76243; max is 65536. By package:
        17 android.app
         3 android.appwidget
        52 android.content
         6 android.content.res
        32 android.gesture
        12 android.inputmethodservice
         3 android.media
         1 android.net
        14 android.opengl
         5 android.os
        66 android.preference
         2 android.telephony
         2 android.text
         8 android.util
       239 android.view
       833 android.widget
        12 java.lang
         2 java.util.concurrent.atomic
         4 org.scaloid
     74888 org.scaloid.common
        16 scala
         1 scala.collection
         7 scala.collection.mutable
         1 scala.reflect
        17 scala.runtime
        at com.android.dx.dex.file.MemberIdsSection.orderItems(MemberIdsSection.java:49)
        at com.android.dx.dex.file.UniformItemSection.prepare0(UniformItemSection.java:73)
        at com.android.dx.dex.file.Section.prepare(Section.java:214)
        at com.android.dx.dex.file.DexFile.toDex0(DexFile.java:506)
        at com.android.dx.dex.file.DexFile.toDex(DexFile.java:216)
        at com.android.dx.command.dexer.Main.writeDex(Main.java:578)
        at com.android.dx.command.dexer.Main.run(Main.java:221)
        at com.android.dx.command.dexer.Main.main(Main.java:174)
        at com.android.dx.command.Main.main(Main.java:91)
    

    I'm wondering at the cause of all this, but I haven't gone through all the changes between the version I used to have and this one. Is there a way we can reduce that method count? Any workaround?

    (Note: Using Proguard works, of course, but this is not the point of my issue...)

    opened by fxthomas 14
  • Add gradle support

    Add gradle support

    I'm not sure if this is a Gradle issue (I know at one stage it had incompatibly between the scala and android plugins because the android plugin actually uses the java plugin)

    That being said there have been a few projects that enabled it, but have since fallen out of date

    Gradle is becoming more popular rapidly, and releasing version just as fast. And the only thing stopping me from using Scaloid and Scala for Android.

    opened by rijnhard 12
  • Passing typed parameters between activities

    Passing typed parameters between activities

    I'm very new to scaloid and scala but this got-typed-everything approach provided by scala is what makes development more controllable and thus exciting to me.

    So when I was unable to find any scaloid-way of passing typed parameters between activities I've made my own. In my BaseActivity class I declared this two functions

    def startActivityWithParam[A](o: Any)
    def getParam[T: ClassTag]: T 
    

    which goes like this

    class aActivity extends BaseActivity{
    ....
    def onClick = {
    val user = new User
    startActivityWithParam[bActivity](user)
    }
    ....
    }
    
    
    class bActivity extends BaseActivity{
    ...
    onCreate {
    val u : User = getParam[User]
    }
    }
    
    class BaseActivity extends SActivity
    

    I've found this pair of functions very convinient so I thought it would be cool to make them part of SActivity (rather TraitContext but anyway) itself.

    I'll be glad to hear any feedback on this.

    opened by aafa 11
  • org.scaloid.common.runOnUiThread

    org.scaloid.common.runOnUiThread

    org.scaloid.common.runOnUiThread currently has the signature:

    def runOnUiThread[T >: Null](f: ⇒ T): T
    

    If it is called with a block that returns Unit, and since Unit <: AnyVal and Null <: AnyRef, Scala can only infer T to be Any, which gives an annoying warning message that Any is inferred if compiling with -Xlint enabled.

    The real issue here however is that I believe returning the result of f or null depending on whether runOnUiThread is called on the UI thread is a misdesign. The caller either calls this function from a non-UI thread to perform side effects on the UI thread, and in this case f should return Unit. If the caller is already on the UI thread, he can just call f directly to collect the result. Also, returning null is basically asking for NullPointerException if the user is not careful about this.

    Therefore, my proposal is to change the signature to the simpler:

    def runOnUiThread(f: => Unit): Unit
    

    and remove the return statements so that runOnUiThread is only used to perform the side effects of f. I can send a pull request if you are okay with this change.

    opened by PkmX 9
  • TableRows are not converted correctly

    TableRows are not converted correctly

    Hey! I was trying to use the library, but I am not able to create TableRows correctly.

    I went to the layout converter and It does not take in account the views inside the TableRow.

    Do you have a working example of STableRow?

    Thanks

    major 
    opened by pasviegas 9
  • Create a template for sbt

    Create a template for sbt

    I've recently created a template project for maven:

    https://github.com/pocorall/hello-scaloid-maven

    Being new to sbt and android-plugin for sbt, I could not find a tangible sbt example that runs Scala on android.

    It is appreciated that if someone contribute a template Scaloid project for sbt.

    enhancement 
    opened by pocorall 9
  • Include implicit ExecutionContext to avoid dynamic invocation problems

    Include implicit ExecutionContext to avoid dynamic invocation problems

    I recently ran into this problem that you also discovered—and found a solution for! It works great, but perhaps to avoid confusion in the future, it would be helpful to have

    implicit val exec = ExecutionContext.fromExecutor(
      new ThreadPoolExecutor(100, 100, 1000, TimeUnit.SECONDS,
        new LinkedBlockingQueue[Runnable]))
    

    in scope whenever someone imports org.scaloid.common._.

    opened by RyanGlScott 8
  • Problem during compile

    Problem during compile

    I followed Builiding Scaloid wiki page, I've installed API 10 in android_home folder, evironment setted, and I can''t compile commons:

    version 3.6.1 sbt scaloid:generate sbt +publishLocally

    and I get compile error

    [info]  delivering ivy file to /home/mateusz/projects/scaloid/scaloid-util/target/scala-2.10/ivy-3.6.1-10.xml
    [error] /home/mateusz/projects/scaloid/scaloid-common/src/main/scala/org/scaloid/common/preferences.scala:144: ']' expected but '.' found.
    [error]         case v: Array[android.os.Parcelable] => i.putExtra(name, v)
    [error]                              ^
    [error] /home/mateusz/projects/scaloid/scaloid-common/src/main/scala/org/scaloid/common/preferences.scala:144: ']' expected but '.' found.
    [error]         case v: Array[android.os.Parcelable] => i.putExtra(name, v)
    [error]                              ^
    

    Could You tell me why ? And what I can do for resolve this problem ? remove fragment od template?

    opened by Morgaroth 6
  • Migrate template engine to Twirl

    Migrate template engine to Twirl

    Although StringTemplate is simple & fast, it must be great if we can use a Scala based, type safe template engine such as Twirl.

    The priority is not that high though.

    major 
    opened by guersam 6
  • no method getVisibleTitleHeight in android.webkit.WebView

    no method getVisibleTitleHeight in android.webkit.WebView

    Proguard warning:

    Reading library jar [/usr/local/android-sdk/platforms/android-20/android.jar] Initializing... Warning: org.scaloid.common.TraitWebView$class: can't find referenced method 'int getVisibleTitleHeight()' in library class android.webkit.WebView

    seems getVisibleTitleHeight has been removed? or it's hidden.

    opened by fishmacs 2
  • How to initialize LocalServiceConnection from a fragment

    How to initialize LocalServiceConnection from a fragment

    I have code like:

    class MapFragment extends Fragment with Contexts[Fragment] with SharedPreferences.OnSharedPreferenceChangeListener {
    
      implicit var ctx:Context = _
    
      override def onActivate(a:Activity) {
        ctx = a
        val locationService = new LocalServiceConnection[LocationService]
        locationService.onConnected {
    ...
    

    But I get:

    /home/nolan/Projects/Hermes/src/main/scala/android/ui.scala:371: could not find implicit value for parameter reg: org.scaloid.common.Registerable
    Error occurred in an application involving default arguments.
        val locationService = new LocalServiceConnection[LocationService]
                              ^
    

    How do I make this custom Fragment a Registerable?

    Thanks.

    opened by ndarilek 2
  • String resources getting mixed up when using Java + Scaloid?

    String resources getting mixed up when using Java + Scaloid?

    I am finding that one subsequent compiles while using Scaloid, some of my string resources are getting mixed up, i.e., seeing the wrong string for a R.string.xx resource ID.

    Doing a clean build fixes the issue, though tedious to do that on every change. Removing Scaloid/Scala from the project makes the issue disappear, though I'd really like to use it.

    opened by catsynth 2
  • Event gravity does not take parameters or application crashs

    Event gravity does not take parameters or application crashs

    Hi

    I want create a Layout. Using the converter I get this code: contentView = new SVerticalLayout { STextView("Large Text").<<.wrap.gravity(Gravity.CENTER_HORIZONTAL).>>.textAppearance(android.R.attr.textAppearanceLarge) this += new SLinearLayout {}.visibility(View.VISIBLE).orientation(LinearLayout.HORIZONTAL) SButton("New Button").<<.wrap.gravity(Gravity.CENTER_HORIZONTAL).>> SButton("New Button").<<.wrap.gravity(Gravity.CENTER_HORIZONTAL).>> SButton("New Button").<<.wrap.gravity(Gravity.CENTER_HORIZONTAL).>> }.<<.fill.>>

    but with this code I get this error Int does not take parameters [error] SButton("New Button").<<.wrap.gravity(Gravity.CENTER_HORIZONTAL).>>

    If I change it to this

    contentView = new SVerticalLayout { STextView("Large Text") textSize 24.dip this += new SLinearLayout{}.fill }

    The app crashs.

    What is the problem?

    Thank you

    opened by ASchmidt84 9
Releases(4.2)
  • 4.2(Mar 9, 2016)

    This version fixes a concurrency issue about LocalServiceConnector. I found the service is not accessed consistently when it is accessed from multiple threads, especially the device does not have sufficient free memory. This fix is verified in a production app, Soundcorset, and I am assured it is working correctly now.

    Additionally, I fixed some of reported bugs.

    Source code(tar.gz)
    Source code(zip)
Data structures in kotlin that maintain order

Ordered Data Structures I came from C++ and fell in love with kotlin. I used the C++ stdlib a lot. I have really been wanted to reach for map and unor

Kyle McBurnett 0 Nov 1, 2021
Project build to understand the concepts of Git and GitHub

Project build to understand the concepts of Git and GitHub. Summarizing the concepts learnt. Created a project in Android Studio Before installing And

Rifah A Khan 0 Oct 6, 2021
Kotlin snippets that you can understand quickly, using only stdlib functionality.

Support this and other projects from Ivan Mwiruki here. 30 seconds of Kotlin Curated collection of useful Kotlin 1.3 snippets that you can understand

Ivan Moto 246 Dec 19, 2022
Jetpack Compose for Desktop and Web, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.

Jetpack Compose for Desktop and Web, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.

JetBrains 10k Jan 7, 2023
A project that takes advantage of docker and makes the load test easier

Performance Test It's a project that takes advantage of docker and makes the load test easier. Also, it collects metrics from each running container.

jorge cabrera 1 Dec 9, 2021
Easy app for managing your files without ads, respecting your privacy & security

Simple File Manager Can also be used for browsing root files and SD card content. You can easily rename, copy, move, delete and share anything you wis

Simple Mobile Tools 1.2k Dec 29, 2022
Tools for Kotlin/Kscript to easy write shell command line in kotlin code

Kscript Tools Easy way to run shell command line in kotlin and other tools Usage Used in kscript: @file:DependsOn("com.sealwu:kscript-tools:1.0.2") Us

Seal 4 Dec 12, 2022
Advent of Code 2021 in Kotlin, solved by myself. Focus on code readability. With GitHub Actions all puzzles are executed and their solutions printed

Advent of Code 2021 in Kotlin Focus on Code Readability. With CI Solution Printing. Welcome to the Advent of Code1 Kotlin project created by michaeltr

Michael Troger 1 Dec 12, 2021
Kotlin Example of how to organize your code using MVC and some patterns seen in class

Kotlin Example of how to organize your code using MVC and some patterns seen in class

José Luis González Sánchez 3 Mar 23, 2022
TakeNotes, taking care of your tasks and your health

Take Notes - Para tornar sua rotina mais Saudável TakeNotes, cuidando de suas tarefas e de sua saúde Sobre • Funcionalidades • Layout • Como executar

null 0 Dec 7, 2021
Simple view which allow you to customise your pizza's toppings and size as per your choice.

TwistedPizzaToppingsView Overview Simple view which allows options to customize your pizza toppings and size as per your choice. Features Android 12 s

MindInventory 19 Dec 18, 2022
To help to promote your android app by prompting users to rate your app in a bottom Sheet.

RateBottomSheet This an Android library to help to promote your Android App by prompting users to rate your app in the Google Play Store with a materi

Farham Hosseini 5 Jul 8, 2022
Binding your extras more easier, more simpler for your Android project

Ktan Ktan make your intent / arguments more easier and readable. And most important, will help you bind all extras for your activity / fragment. And a

Ade Fruandta 3 Jan 7, 2023
Amazing and easy to use Accordion Library for Android built with kotlin

AccoLib An easy-to-use, amazing Accordion Library for Android built with kotlin. It reduces the amount of code needed to make Accordions in android, w

Gourav Khunger 6 Jul 4, 2022
A custom view for rating which easy to make and use, but function is excellent

QRatingView A custom view for rating which easy to make and use, but function is excellent Effect Picture Properties <declare-styleable name="QRat

QCoder 1 Dec 3, 2021
Sample app to demonstrate the integration code and working of Dyte SDK for android, using Kotlin.

Dyte Kotlin Sample App An example app in kotlin using the Dyte Mobile SDK Explore the docs » View Demo · Report Bug · Request Feature Table of Content

Dyte 8 Dec 3, 2021
A sample code taking picture in android with app permission and simple ui

Android Taking Picture Sample Just sample code taking picture in android with ap

M. Hafizh Anbiya 2 Dec 29, 2021
A basic library that enables easy composition of gesture sequence recognition on a view

GestureSequence A basic library that enables easy composition of gesture sequence recognition on a view. Basic API looks like: // Perform action() whe

Paul Klauser 8 Nov 2, 2022