Scala on Android - Layout and Styles
by Javi Pacheco
- •
- July 06, 2015
- •
- scala• android• code tutorials• scala on android
- |
- 11 minutes to read.

Previously in our Scala on Android Series, we discussed how to prepare the environment and setting SBT configuration. This time we’ll review how this works with Layouts and our proposal that uses functional programming inside the UI.
For our purposes, we made a project on GitHub to show you the different steps for starting in Scala on Android. We’ve included an SBT groll plugin that you can use to navigate commits and learn step-by-step using:
> groll next
The app is simple. It shows different users in a list with their name and age. We’ve preloaded the list with several default users and used random.org to create random users from the Internet for us.
Let’s look at the piece of code that’s working with layouts in this project.
#Plain old Android way - Imperative Style Layouts and styles can be created in the same manner as Android using Java, but now you’re able to utilize the additional advantages of Scala.
Our project uses a RecyclerView to show different users. We’ve created an XML Layout like this:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/message"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/downloading"
android:text="@string/downloading"
android:layout_gravity="bottom"
android:padding="@dimen/padding_default"
android:textColor="@color/text_loading"
android:background="@color/background_loading"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
The FrameLayout contains different widgets for showing the information. A RecyclerView is utilized for loading information, a TextView for showing an error message, if it’s necessary, and another TextView aligned at the bottom for showing a loading message when downloading information from the Internet.
This next piece of code is where we load the XML Layout in our activity. I’m sure that if you’ve worked in Android before, this code will look very familiar:
class MainActivity
extends AppCompatActivity
with «TypedFindView»
with ComponentRegistryImpl {
lazy val recyclerView = findView(TR.recyclerview)
lazy val message = findView(TR.message)
lazy val downloading = findView(TR.downloading)
override def onCreate(bundle: Bundle) {
super.onCreate(bundle)
setContentView(R.layout.main)
recyclerView.setHasFixedSize(true)
recyclerView.setAdapter(UserAdapter(users))
recyclerView.setLayoutManager(new LinearLayoutManager(this))
}
}
»SBT Android plugin trait|Help to find views in our main layout«
Previously, we talked about the android-sdk-plugin, which can offer some additional helpful features here. First off is the trait TypedFindView
. The plugin generates a typed resource called TR
(similar to R
class in Android), and you can use it in the method findView
to avoid casting the variables. Moreover, Scala
provided lazy vals
that allows you to remove ButterKnife (if you use it) from your project.
For this particular case, we’re using the Cake Pattern for dependency injection (you can learn more about Cake Pattern in Jonas Bonér’s post. Our ApiService gets two calls, a random string for name and a random integer for age and returns a User
object.
Another interesting and relevant piece of code is as follows:
override def addUser: Future[User] = for {
Some(name) <- apiService.readName(getString(R.string.api_read_name))
Some(age) <- apiService.readAge(getString(R.string.api_read_age))
} yield User(name, age)
We are using a for comprehension
to work with several async tasks and you can use this service from your activity:
private[this] def addItem() = {
downloading.setVisibility(View.VISIBLE)
userService.addUser map {
user =>
«runOnUiThread»(new Runnable {
override def run(): Unit = addUserToList(user)
})
} recover {
case _ => showErrorMessage()
}
}
»From Android SDK|Runs the specified action on the UI thread«
You have to use runOnUiThread
method to ensure that you are working on the UI Thread. There are better options for doing this, but we’ll learn those later.
We have learned to create our UI and use Future
s in async tasks to transform our views.
This is the method used in Imperative Programming. In the next section, we’ll learn how we can work using functional programming. Are you ready?
Functional way. Programming your UI from functional a perspective
We love functional programming, so why are we not using it in our UI? We’ve previously talked about Macroid, and according to Macroid’s website: “Macroid is the most badass modular functional user interface creation language for Android, implemented with Scala macros.” It’s also amazing being able to create interfaces using a functional way. Why?
- You can compose your UI Actions and launch them when you need it
- You have a simple DSL to work with your widgets
- You can work with animations easily
- You can use pattern matching to find views in your layouts
RecyclerView example using Macroid
We’re going to give you a similar example as above, but now using Macroid. For that, we’re going to use the Scala Days Official App (you can download from Google Play or if you prefer to compile the code, cloning the project from GitHub).
If you have already downloaded the project, you may have noticed something weird. The project doesn’t have XML files for layouts and styles. Macroid creates the UI using macros in a really simple way. It’s the same, but they are not expressed via the Android XML traditional layout files. In Scala Days, we are using lists on different screens like Schedule, Speakers, Social, and Sponsors. We have a trait called ListLayout where we create these lists.
trait ListLayout {
var recyclerView = slot[RecyclerView]
var progressBar = slot[ProgressBar]
def content(implicit context: ActivityContextWrapper) = getUi(
l[FrameLayout](
w[ProgressBar] <~ «wire»(progressBar) <~ progressBarStyle,
w[RecyclerView] <~ wire(recyclerView) <~ recyclerViewStyle
) <~ rootStyle
)
}
»Macroid|Connect your view to a variable«
You only have to use the getUi
method and add your widgets and layouts. You have to use l
for ViewGroup
s (e.g l[FrameLayout]
) and w
for your widgets (e.g w[TextView]
or w[ImageView]
)
But, what’s <~
? Easy. You can change some properties of your widget using this symbol and it’s called Tweaks. For example, you can use w[TextView] <~ text("Hello!")
to change the text to your TextView. We always use a trait for all styles of our views. You can see this in the code progressBarStyle
or recyclerViewStyle
, these methods are in ListStyles trait
trait ListStyles {
val recyclerViewStyle: Tweak[RecyclerView] = «vMatchParent» + «rvNoFixedSize»
val progressBarStyle: Tweak[ProgressBar] = «vWrapContent» + «flLayoutGravity»(Gravity.CENTER)
}
»Macroid-Extras|Width and height match parent«
»Macroid-Extras|Property for RecyclerView«
»Macroid-Extras|Width and height wrap content«
»Macroid-Extras|Property for FrameLayout«
Macroid has a few tweaks created but you can use macroid-extras library, where we have created a bunch of tweaks for you to use in your views.
Ok, this all seems nice, but it’s possible that you’re wondering about wire
in the code. Do you remember findViewById
d? You can connect a view with a field in your class, and you can use it later, but there is a really important thing to note: slot
is an Option[Widget]
with a value of None
. Your code will be better and safer. Why? When you apply your UI Action in your views, if the view wasn’t created or was destroyed, the UI Action will never be launched. Goodbye, NullPointersExceptions
.
Finally, we’re going to learn how to insert our new layout in the activity or fragment and change it later. For that, we have selected the Speaker Screen in the Scala Days App because it’s simpler than others. You can see the code in SpeakersFragment:
class SpeakersFragment
extends Fragment
with Contexts[Fragment]
with ListLayout {
override def onCreateView(i: LayoutInflater, c: ViewGroup, a: Bundle): View = «content»
override def onViewCreated(view: View, savedInstanceState: Bundle): Unit = {
super.onViewCreated(view, savedInstanceState)
runUi(
(recyclerView
<~ rvLayoutManager(new LinearLayoutManager(context))) ~
loadSpeakers() ~
(reloadButton <~ «On.click»(
loadSpeakers(forceDownload = true)
)))
}
»ListLayout trait|Method defined in ListLayout«
»Macroid|UI Action clicking button«
You can see that onCreateView
returns the previous content
method (remember that we’ve included the ListLayout
trait). In an Activity
, we’ll use setContentView(content)
.
In onViewCreated
you can see how we are transforming the views using runUi
. With this method of Macroid, we are forcing your UI Actions in the Android UI Thread. You can combine it in Future
for ensure that you are making your changes in the right thread. You also can use the ~
symbol for combining different widgets or UI Action in the runUi
method.
This is good so far, but we want to let you in on some more amazing things :-)
Animations
In our discussion about animations, we’re going to show you another project called Scala API Demos (you can download from Google Play or clone from GitHub). The project has several examples using Scala on Android; each example lists the level of difficulty with both Scala and Android.
In order to explain animations, we’re going to show you the code of the example Ripple Background. You can see the animation in the next image:
When you click on the buttons, 3 animations are produced:
- The circle moves to the center of rectangle
- Ripple animation discovers the new color
- Fade for making the circle appear in the bottom
If you have worked with animations in Android, it’s possible that you think that you have to concat the 3 animations with AnimatorListenerAdapter. The code in Scala using Macroid is the next thing we’ll look at:
((circleView <~~ move(rippleBackground)) ~~ (rippleBackground <~~ ripple(rippleData)) ~~ (circleView <~~ fadeIn(1000))
Magic! Maybe, or maybe not. In Macroid, the animations are called Snails, and you have to use the <~~
symbol for that. For example circleView <~~ fadeIn(1000)
creates a new fade-in for the circle selected during one second. You can use the ~~
operator to concat different animations. We are going to remember the three animations:
- The circle moves to center of rectangle:
circleView <~~ move(rippleBackground)
- Ripple animation discovers the new color:
rippleBackground <~~ ripple(rippleData)
- Fade for making the circle appear in the bottom:
circleView <~~ fadeIn(1000)
The left side is the view where we are going to apply the animation (remember slot[Widget]
), but what’s the right side? Simple, the right side is the Snail. You can create your own snails and use it in different views in your app. The next code is the fadeIn
method used in the 3rd animation:
def «anim»(animation: Animation, duration: Long = -1L) = Snail[View] { x ⇒
»Snail method|General snail for animate view«
val animPromise = Promise[Unit]()
animation.setAnimationListener(new AnimationListenerAdapter {
override def onAnimationEnd(a: Animation) { animPromise.complete(Success(())) }
})
if (duration >= 0) animation.setDuration(duration)
x.startAnimation(animation)
animPromise.future
}
def «fadeIn»(millis: Long) = show ++ anim(new AlphaAnimation(0, 1), duration = millis)
»Fade In|Snail and Tweak composition«
So no, it’s not magic. We should create the animations using Android SDK, but you can compose your animations easily in different parts of your application.
The anim
method is the Snail. You only have to create a Promise
like the example and call success
when the animation is finished. The magic is the responsibility of Macroid.
The fadeIn
method has an interesting function allowing you to combine tweaks and snails. In this example, when you add fadeIn
to your code, we first show the view (like view.setVisibility(VISIBLE)
) and then create the fade-in animation later. For that, we can use ++
operator for snails and tweak composition.
Conclusion
Scala is amazing for creating a new DSL for your UIs, letting you simplify your code and reuse it in your app. Not only will your code be elegant, removing the listeners will make it easier to understand.
If you want to try it, we invite you to create a new example in the Scala API Demos project. Send us a PR and we’d be happy to help you along the way!