Solutions to the Exercises
The following are the solutions to the exercises given in the chapters.
Chapter
Exercise 1:
Only (2) is correct. Version (1) uses round brackets for the body, but curly brackets are needed. Version (3) is technically correct but does not use camelcase notation for the class name, and Version (4) tries to use a space in the class name.
Exercise 2:
Only (4) is correct. Version (1) uses an undefined “
variable
” keyword. Version (2) uses an undefined “
property
” keyword. Version (3) does not yield to the
propertyName:PropertyType
property notation. Version (5) does not specify a mutability keyword (
var
or
val
).
Exercise 3:
A
val
corresponds to an immutable (unchangeable) variable. Setting it to
0.0
means it will never have a different value, which makes no sense for an invoice. This can be fixed by using
var
instead of
val
.
Exercise 4:
It is not allowed to change a
val
property. The property gets first set by the parameter declaration part of the class declaration and cannot be changed afterward.
Exercise 5:
The property
blue
must be initialized as well, either in the property declaration or inside the
init{ }
block.
Exercise 6:
The class reads:
class Invoice(val buyerFirstName:String,
val buyerLastName:String,
val date:String,
val goodName:String,
val amount:Int,
val pricePerItem:Double)
{
val buyerFullName:String =
buyerFirstName + " " + buyerLastName
val totalPrice:Double =
amount * pricePerItem
}
Exercise 7:
The method reads:
fun goodInfo():String {
return amount.toString() + " pieces of "
+ goodName
}
or
fun goodInfo():String =
amount.toString() + " pieces of " + goodName
Using string interpolation you can also use
fun goodInfo():String =
"${amount} pieces of ${goodName}"
Here the
.toString()
is implied.
Exercise 8:
The class reads:
class Person(var firstName:String,
var lastName:String,
var ssn:String,
var dateOfBirth:String,
var gender:Char)
An empty body
{ }
can be added, but because it is empty it is optional.
Exercise 9:
The class instantiation reads:
val person1 = Person("John", "Smith", "0123456789", "1997-10-23", 'M')
Exercise 10:
Either add
class GameUser(val firstName:String,
val lastName:String,
val birthday:String,
val userName:String,
val registrationNumber:Int,
val userRank:Double) {
}
to the end of the
MainActivity.kt
file, or create a file
GameUser.kt
inside the same folder as file
MainActivity.kt
. As its contents, you must repeat the
package
declaration.
package kotlinforandroid.book.numberguess
class GameUser(val firstName:String,
val lastName:String,
val birthday:String,
val userName:String,
val registrationNumber:Int,
val userRank:Double) {
}
Exercise 11:
The class instantiation using named parameters reads:
val person1 = Person("firstName = John",
lastName = "Smith",
ssn = "0123456789",
dateOfBirth = "1997-10-23",
gender = 'M')
The argument sort order is free.
Exercise 12:
Add
var gameUser = GameUser(lastName = "Doe",
firstName = "John",
userName = "jdoe",
birthday = "1900-01-01",
registrationNumber = 0,
userRank = 0.0 )
right underneath
var tries = 0
Exercise 13:
The new class with default SSN parameter in the constructor reads:
class Person(var firstName:String,
var lastName:String,
var ssn:String = "",
var dateOfBirth:String,
var gender:Char)
An instantiation using this default value would read:
val person1 = Person("firstName = John",
lastName = "Smith",
dateOfBirth = "1997-10-23",
gender = 'M')
where the sort order of the arguments is free.
Exercise 14:
The class declaration now reads:
class GameUser(val firstName:String,
val lastName:String,
val userName:String,
val registrationNumber:Int,
val birthday:String = "",
val userRank:Double = 0.0) {
}
Exercise 15:
The class with the secondary constructor added reads:
class Person(var firstName:String,
var lastName:String,
var ssn:String,
var dateOfBirth:String,
var gender:Char)
{
constructor(firstName:String,
lastName:String,
ssn:String,
gender:Char) : this(firstName,
lastName, ssn, "000-00-00", gender)
}
To perform an instantiation, for example, write:
val person1 = Person("John", "Smith",
"0123456789", 'M')
But you can also use named parameters:
val person1 = Person(firstName = "John",
lastName = "Smith",
ssn = "0123456789",
gender = 'M')
Exercise 16:
None.
Exercise 17:
The object declaration reads:
object Constants {
val numberOfTabs = 5
val windowTitle = "Astaria"
val prefsFile = "prefs.properties"
}
Property types are not needed; Kotlin can infer them from the literals right of the ”=” sign. The diagnostic code looks like this:
fun main(args:Array<String>) {
println(
"Number of tabs: " +
Constants.numberOfTabs +
"
Window title: " +
Constants.windowTitle +
"
Prefs file: " +
Constants.prefsFile
)
}
The formatting is, of course, up to you.
Exercise 18:
The class at a bare minimum reads
class Triangle() {
companion object {
val NUMBER_OF_CORNERS = 3
}
fun info() {
println("Number of corners: " +
NUMBER_OF_CORNERS)
}
}
Exercise 19:
The code reads:
fun main(args:Array<String>) {
val triangle = Triangle()
val numberOfCorners =
Triangle.NUMBER_OF_CORNERS
}
Exercise 20:
The code reads:
interface ElementaryParticle {
fun mass():Double
fun charge():Double
fun spin():Double
}
class Electron : ElementaryParticle {
override fun mass() = 9.11e-31
override fun charge() = -1.0
override fun spin() = 0.5
}
class Proton : ElementaryParticle {
override fun mass() = 1.67e-27
override fun charge() = 1.0
override fun spin() = 0.5
}
Exercise 21:
(2), (3), (4), and (5) are true; (1) and (6) are false. For (1): Interfaces can never be instantiated. For (6): An electron is not a proton. (5) is possible because both are
ElementaryParticle
s and we have a
var
that allows reassignment.
Exercise 22:
The interface plus implementations code reads:
interface RandomNumberGenerator {
fun rnd(minInt:Int, maxInt:Int)
}
class StdRandom : RandomNumberGenerator {
override fun rnd(minInt: Int, maxInt: Int):Int {
val span = maxInt - minInt + 1
return minInt + Math.floor(Math.random()*span).toInt()
}
}
class RandomRandom : RandomNumberGenerator {
val rnd:Random = Random()
override fun rnd(minInt: Int, maxInt: Int):Int {
val span = maxInt - minInt + 1
return minInt + rnd.nextInt(span)
}
}
Put that at the end of
MainActivity.kt
, or put it into a new file using, for example, the name
random.kt
.
The new property in the activity reads
val rnd:RandomNumberGenerator = StdRandom()
// or ... = RandomRandom()
The new code in method
start(
...
)
reads
number = rnd.rnd(Constants.LOWER_BOUND, Constants. UPPER_BOUND)
(the
val span =
... line can be removed).
Exercise 23:
Over the “java” item on the left-side pane (Android view), press mouse-right and then New → Kotlin File/Class for classes, interface, or singleton objects. Or choose New → Package for packages (folders). If asked, select the “main/java” branch. For all classes, interfaces, and singleton objects you can always first create a “class” type file and then accordingly adapt its contents.
Exercise 24:
The listings read:
MainActivity.kt
package kotlinforandroid.book.numberguess
import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.widget.ScrollView
import android.widget.TextView
import kotlinforandroid.book.numberguess.common.Constants
import kotlinforandroid.book.numberguess.model.GameUser
import kotlinforandroid.book.numberguess.random.RandomNumberGenerator
import kotlinforandroid.book.numberguess.random.impl.StdRandom
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*
class MainActivity : AppCompatActivity() {
val rnd: RandomNumberGenerator = StdRandom()
// or ... = RandomRandom()
var started = false var number = 0
var tries = 0
var gameUser = GameUser(
lastName = "Doe",
firstName = "John",
userName = "jdoe",
birthday = "1900-01-01",
registrationNumber = 0,
userRank = 0.0
)
override
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fetchSavedInstanceData(savedInstanceState)
doGuess.setEnabled(started)
}
override
fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
putInstanceData(outState)
}
fun start(v: View) {
log("Game started")
num.setText("")
started = true
doGuess.setEnabled(true)
status.text = getString(R.string.guess_hint,
Constants.LOWER_BOUND,
Constants.UPPER_BOUND)
number = rnd.rnd(Constants.LOWER_BOUND, Constants.UPPER_BOUND)
tries = 0
val r = Random()
r.nextInt(7)
}
fun guess(v:View) {
if(num.text.toString() == "") return
tries++
log("Guessed " + num.text +
" (tries:" + tries + ")")
val g = num.text.toString().toInt()
if(g < number) {
status.setText(R.string.status_too_low)
num.setText("")
} else if(g > number){
status.setText(R.string.status_too_high) num.setText("")
} else {
status.text = getString( R.string.status_hit, tries)
started = false
doGuess.setEnabled(false)
}
}
//////////////////////////////////////////////////
//////////////////////////////////////////////////
private
fun putInstanceData(outState: Bundle?) {
if (outState != null) with(outState) {
putBoolean("started", started) putInt("number", number)
putInt("tries", tries)
putString("statusMsg", status.text.toString())
putStringArrayList("logs", ArrayList(console.text.split("
")))
}
}
private
fun fetchSavedInstanceData(
savedInstanceState: Bundle?) {
if (savedInstanceState != null)
with(savedInstanceState) {
started = getBoolean("started")
number = getInt("number")
tries = getInt("tries")
status.text = getString("statusMsg")
console.text =
getStringArrayList("logs")!!.
joinToString("
")
}
}
private fun log(msg:String) {
Log.d("LOG", msg)
console.log(msg)
}
}
Constants.kt
package kotlinforandroid.book.numberguess.common
object Constants {
val LOWER_BOUND = 1
val UPPER_BOUND = 7
}
Console.kt
package kotlinforandroid.book.numberguess.gui
import android.content.Context
import android.util.AttributeSet
import android.widget.ScrollView
import android.widget.TextView
class Console(ctx: Context, aset: AttributeSet? = null) :
ScrollView(ctx, aset) {
companion object {
val BACKGROUND_COLOR = 0x40FFFF00
val MAX_LINES = 100
}
val tv = TextView(ctx)
var text:String
get() = tv.text.toString()
set(value) { tv.setText(value) }
init {
setBackgroundColor(BACKGROUND_COLOR)
addView(tv)
}
fun log(msg:String) {
val l = tv.text.let {
if(it == "") listOf() else it.split("
")
}.takeLast(MAX_LINES) + msg
tv.text = l.joinToString("
")
post(object : Runnable {
override fun run() {
fullScroll(ScrollView.FOCUS_DOWN)
}
})
}
}
You also must adapt the element in
activity_main.xml
:
...
<kotlinforandroid.book.numberguess.gui.Console
android:id="@+id/console"
android:layout_height="100sp"
android:layout_width="match_parent" />
...
GameUser.kt
package kotlinforandroid.book.numberguess.model
class GameUser(val firstName:String,
val lastName:String,
val userName:String,
val registrationNumber:Int,
val birthday:String = "",
val userRank:Double = 0.0) {
}
RandomNumberGenerator.kt
package kotlinforandroid.book.numberguess.random
interface RandomNumberGenerator {
fun rnd(minInt:Int, maxInt:Int):Int
}
RandomRandom.kt
package kotlinforandroid.book.numberguess.random.impl
import kotlinforandroid.book.numberguess.random.
RandomNumberGenerator
import java.util.*
class RandomRandom : RandomNumberGenerator {
val rnd: Random = Random()
override fun rnd(minInt: Int, maxInt: Int):Int {
val span = maxInt - minInt + 1
return minInt + rnd.nextInt(span)
}
}
StdRandom.kt
package kotlinforandroid.book.numberguess.random.impl
import kotlinforandroid.book.numberguess.random.
RandomNumberGenerator
class StdRandom : RandomNumberGenerator {
override fun rnd(minInt: Int, maxInt: Int):Int {
val span = maxInt - minInt + 1
return minInt +
Math.floor(Math.random()*span).toInt()
}
}
Chapter
Exercise 1:
The
var
or
val
in the constructor parameter is missing, so the color is not transported to a
property
. In this case we have to use
var
, because we want to change it:
class Triangle(var color: String) {
fun changeColor(newColor:String) {
color = newColor
}
}
Exercise 2:
The code reads:
class A {
var a:Int = 1 // A
init {
a = 2 // B
}
fun b() {
a = 3 // C
}
}
fun main(args:Array<String>) {
val a = A()
a.a = 4 // D
}
Exercise 3:
Write
val a = 42
val s = "If we add 4 to a we get ${a+4}"
Exercise 4:
Only (4) is allowed.
Exercise 5:
It is not allowed to change method parameter variables.
Exercise 6:
A
return
without argument is allowed for methods not returning anything, so the method is valid. The
return
at the end is superfluous though.
Exercise 7:
Yes.
Exercise 8:
Use the expression variant:
class A(val a:Int) {
fun add(b:Int) = a + b
fun mult(b:Int) = a * b
}
Exercise 9:
The interface reads:
interface AInterface {
fun add(b:Int):Int
fun mult(b:Int):Int
}
Exercise 10:
The output is
meth1: 42 7
Exercise 11:
The code reads:
For using companion objects an instance of the class is not needed!
Exercise 12:
The code reads:
val p = Person()
p.setName(lName = "Doe", fName = "John")
// or
p.setName(fName = "John", lName = "Doe")
Exercise 13:
The method declaration reads:
fun set(lastName:String = "",
firstName:String = "",
birthDay?:String = null,
ssn:String? = null) { ... }
And for the invocation write:
set(lastName = "Smith", ssn = "1234567890")
or
set(ssn = "1234567890", lastName = "Smith")
Exercise 14:
The code reads:
class Club {
fun addMembers(vararg names:String) {
println(names.size)
println(names.joinToString(" : "))
}
}
fun main(args:Array<String>) {
var club = Club()
club.addMembers("Hughes, John",
"Smith, Alina",
"Curtis, Solange")
}
Exercise 15:
The output will be:
B.y() -> a = 7
A.q() -> a = 7
Exercise 16:
The output will be:
A.x() : g = 99
B.y() : g = 8
A.q() : g = 99
The property needs to be declared
private
, otherwise
g
from class
A
will be visible in the subclass
B
as well, and then we have two declarations of one property, which the language does not allow.
Exercise 17:
The code then reads:
open class A() {
open var g:Int = 99
fun x() {
println("A.x() : g = ${g}")
}
fun q() {
println("A.q() : g = ${g}")
}
}
class B : A() {
override var g:Int = 8
fun y() {
println("B.y() : g = ${g}") q()
}
}
fun main(args:Array<String>) {
val b = B()
b.x()
b.y()
}
And the output will be
A.x() : g = 8
B.y() : g = 8
A.q() : g = 8
Exercise 18:
Class (3) is invalid, because the local variable gets used before it is declared. Class (5) is invalid, because the declaration of
a
is valid only inside
method1()
. All the other classes are valid.
Exercise 19:
The method calls itself, calls itself, calls itself, and so on, forever. This is called
recursion
and here leads to an error.
Chapter
Exercise 1:
Write
fun add10(a:Int) = a + 10
fun add100(a:Int) = a + 100
Maybe rename the file
util.kt
to avoid the impression that it contained a class or a singleton object. The file name, in fact, no longer plays a role.
The client code then reads:
import com.example.util.*
class A(q:Int) {
val x10:Int = add10(q)
val x100:Int = add100(q)
}
Exercise 2:
Write
package com.example
import java.lang.Math.log
class A {
fun calc(a:Double) = log(a)
}
Exercise 3:
(1) is not true, as the coordinates x and y cannot be swapped. (2) is not true, because the classes don’t match. (3) is true, because the extracted x coordinates match. (4) is true, because the
1
automatically gets converted to
1.0
and after that all coordinates match. (5) is true, as the class and all coordinates match.
Exercise 4:
The
GameUser
class
is a good candidate for a data class. Just prepend
data
to the
class
declaration:
data class GameUser ...
.
Exercise 5:
Write
data class GameUser(val firstName:String,
val lastName:String,
val userName:String,
val registrationNumber:Int,
val gender:Gender = Gender.X,
val birthday:String = "",
val userRank:Double = 0.0) {
enum class Gender{F,M,X}
val fullName:String
val initials:String
init {
fullName = firstName + " " + lastName
initials = firstName.toUpperCase() +
lastName.toUpperCase()
}
}
The enumeration class could also be placed outside the
GameUser
class
. Also, for this exercise whether you use
val
s or
var
s does not play a role.
Exercise 6:
A
set(value)
... does not make sense, because
val
s are immutable. Other than that, for the getters you can do the same as for
var
s
Exercise 7:
Write
val str:String get() = toString()
or
val str get() = toString()
because Kotlin can infer the return type. Do not use
var
instead of
val
, as setting here makes no sense.
Exercise 8:
Write
data class GameUser(var firstName:String,
var lastName:String,
var userName:String,
var registrationNumber:Int,
var gender:Gender = Gender.X,
var birthday:String = "",
var userRank:Double = 0.0) {
enum class Gender{F,M,X}
val fullName:String
get() = firstName + " " + lastName
val initials:String
get() = firstName.toUpperCase() +
lastName.toUpperCase()
}
Both
fullName
and
initials
shouldn’t be
var
s, because they provide derived values.
Exercise 9:
Write
data class Point(val x:Double, val y:double) {
operator fun minus(p2:Point) =
Vector(p2.x-this.x, p2.y-this.y)
}
data class Vector(val dx:Double, val dy:Double) {
operator fun plus(v2:Vector) =
Vector(this.dx + v2.dx, this.dy + v2.dy)
operator fun minus(v2:Vector) =
Vector(this.dx - v2.dx, this.dy - v2.dy)
}
Chapter
Exercise 1:
The code reads:
Math.sqrt(
( a + (b-x)/2 ) / ( b*b - 7*x )
)
You can also use
Math.pow(b, 2.0)
instead of
b * b
Exercise 2:
Write
class Concatenator {
var string:String = ""
fun add(s:String) { string += s }
operator fun contains(other:String) =
string.contains(other)
}
Chapter
Exercise 1:
Texts and formatting are up to you.
Chapter
Exercise 1:
The exception class reads:
package kotlinforandroid.book.numberguess.common
class GameException(msg:String) : Exception(msg)
The
guess()
function
inside class
MainActivity
with the check added reads:
fun guess(v:View) {
if(num.text.toString() == "") return
try {
if (num.text.toString().toInt() <
Constants.LOWER_BOUND)
throw GameException(
"Must guess a number >= " +
"${Constants.LOWER_BOUND}")
if (num.text.toString().toInt() >
Constants.UPPER_BOUND)
throw GameException(
"Must guess a number <= " +
"${Constants.UPPER_BOUND}")
// rest of the original function...
} catch(e:GameException) {
Toast.makeText(this,
"Guessable numbers: " +
"${Constants.LOWER_BOUND} to " +
"${Constants.UPPER_BOUND} ",
Toast.LENGTH_LONG).show()
}
}
Chapter
Exercise 1:
The code reads:
val arr = IntArray(101, { i -> 100 - i })
Exercise 2:
The code reads:
booleanArrayOf(true, false, true)
Exercise 3:
The code reads:
val fruits = mutableSetOf("Apple", "Banana",
"Grape", "Engine")
fruits.remove("Engine")
fruits.add("Cherry")
val fruits5 = fruits.filter {
element -> element.length == 5
}
Exercise 4:
You could write
val sorted = gul.sortedWith(
compareBy(GameUser::lastName)
then
compareBy(GameUser::firstName))
Or, to be a little bit less expressive:
val sorted = gul.sortedWith(
compareBy(GameUser::lastName,
GameUser::firstName))
Exercise 5:
The code reads:
gul.sortWith(
compareBy(GameUser::lastName)
then
compareBy(GameUser::firstName))
or
gul.sortWith(
compareBy(GameUser::lastName,
GameUser::firstName))
Exercise 6:
The code reads:
val groupedByManufacturer = cars.groupBy {
car -> car.vin.substring(0,3)
}
val wxxCars = groupedByManufacturer["WXX"]
Exercise 7:
The code reads:
(1..100).toList().reduce{ acc,v -> acc*v }
Actually the
toList()
can be omitted, as ranges have a
reduce()
as well:
(1..100).reduce{ acc,v -> acc*v }
Exercise 8:
The code reads:
val fruits = listOf("Bananas", "apples",
"Oranges")
val prices = listOf(1.69, 2.19, 2.79)
data class Fruit(
val name:String, val price:Double)
val zipped = fruits.zip(prices,
{ a, b -> Fruit(a, b) })
Chapter
11
Exercise 1:
(1) and (3) are true, and (2) is only true in cases where we implemented an appropriate
equals()
function
.
Exercise 2:
(1) and (2) are true. (3) is true only if function
hashCode()
is implemented appropriately. However, for
==
to work correctly, a
hashCode()
implementation is not needed. It
should
be provided, though, to avoid surprises if a class gets used as a map key.
Chapter
12
Exercise 1:
One possible solution would be:
val f : (String, Int) -> String =
{ s:String, num:Int ->
(1..num).map { s }.joinToString { it } }
We use the range operator
..
to get something that iterates
num
times over whatever, map each iteration to the parameter string, and then concatenate the
num
identical copies of the string.
Exercise 2:
The code reads:
val f : (String) -> String = { it + "!" }
Exercise 3:
The filter reads:
val startsWithL = employees.filter {
it.firstName.startsWith("L") }.toList()
Chapter
13
Exercise 1:
The code reads:
class Quadruple<A,B,C,D>(
var p1:A, var p2:B, var p3:C, var p4:D)
val q1 = Quadruple(1, 2, 3.14, "Hello")
The type and property parameter names, of course, are up to you.
Note that we could have written this:
class Quadruple<A,B,C,D>(
var p1:A, var p2:B, var p3:C, var p4:D)
val q1:Quadruple<Int, Int, Double, String> =
Quadruple<Int, Int, Double, String>(
1, 2, 3.14, "Hello")
but the explicit type parameters could be omitted because Kotlin can infer the type from the literals provided.
Exercise 2:
The code reads:
class Sorter<T : Comparable<T>> {
val list: MutableList<T> = mutableListOf()
fun add(value:T) {
list.add(value)
list.sort()
}
}
Chapter
14
Exercise 1:
The annotation declaration reads:
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class NotNegative()
Inside the
Calculator.Operator
enumeration, add
SQRT(”sqrt”)
Inside the
f?.valueParameters?.forEachIndexed { ...
loop, add
p.findAnnotation<NotNegative>()?.run {
if (params[ind] < 0.0)
throw RuntimeException(
"Parameter ${ind} must be positive")
}
Finally, add a function:
fun sqrt(@NotNegative p:Double) : Double {
return Math.sqrt(p)
}
Chapter
16
Exercise 1:
Write
val sorted = l.sortedBy { empl -> empl.ssn }
or
val sorted = l.sortedBy { it.ssn }
Exercise 2:
Let it read
val map = l.associateBy { empl -> empl.ssn }
or
val map = l.associateBy { Employee::ssn }
Exercise 3:
The output is a list
[1, 2, 3, 4]
Exercise 4:
The output is a list
[“1”, “2”, “3”, “4”]
Exercise 5:
The code reads:
val filtered = l.filter { it.ssn.startsWith("0") }
Exercise 6:
The check reads:
val l = listOf(1, 2, 3, 4)
val allGreaterThanZero = l.all { it > 0 }
Exercise 7:
One possibility is
l.find{ it == 42 }?.run{
throw Exception("42 found!") }
Another possibility reads:
l.contains(42).takeIf { it }?.run {
throw Exception("42 found!") }
Exercise 8:
The solution is
l.sumByDouble { it.weight }
Chapter
17
Exercise 1:
There are several possibilities. We save the “now” instant on creation and deduce a
Duration
object
with twice the elapsed time since that instant:
class ClockTwiceAsFast : Clock() {
val myStartInstant : Instant
init {
myStartInstant = Clock.systemUTC().instant()
}
override
fun withZone(zone: ZoneId?): Clock = this
override
fun getZone(): ZoneId = ZoneId.of("Z")
override fun instant(): Instant {
val dur2 = Duration.between(myStartInstant,
Clock.systemUTC().instant()).multipliedBy(2L)
return myStartInstant.plus(dur2)
}
}
Exercise 2:
Write
operator fun String.rem(regex:String) = this.matches
(Regex(regex))
Chapter
18
Exercise 1:
Start creating the app as a “Basic Activity” app. Android Studio then builds a standard
activity_main.xml
file. The actual user interface elements get defined in
content_main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior=
"@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Multithreaded PI"
android:textSize="25sp"/>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Processors"/>
<TextView android:id="@+id/procs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Iterations"/>
<EditText android:id="@+id/iters"
android:text="1000000"
android:inputType="number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Threads"/>
<EditText android:id="@+id/threads"
android:text="4"
android:inputType="number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Cumul Iters"/>
<TextView android:id="@+id/cumulIters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Current Pi"/>
<TextView android:id="@+id/pi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Calc Time"/>
<TextView android:id="@+id/calcTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<Button android:text="CALC"
android:onClick="calc"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button android:text="RESET"
android:onClick="reset"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
The corresponding activity class already got listed in the floating text. Note that for simplicity, label and button texts are directly entered in the layout file. Instead they should be exported to resource files.
Exercise 2:
Write
...
l111 = launch {
Log.d("LOG", "E")
withTimeout(500L) {
delay(1000L)
}
Log.d("LOG", "F")
delay(1000L)
Log.d("LOG", "G")
}
...
The logging will not differ from the logging with the
cancel().
Chapter
20
Exercise 1:
Write
fun createXmlDocument(): Document =
DocumentBuilderFactory.newInstance().
newDocumentBuilder().newDocument()
Exercise 2:
A possible solution would be:
val root = jsonObjectNodeOf()
with(root) {
put("firstName", "Arthur")
put("lastName", "Doyle")
put("dateOfBirth", "03/04/1997")
put("address",
jsonObjectNodeOf(
"streetAddress" to "21 3rd Street",
"city" to "New York",
"state" to "NY",
"postalCode" to "10021-1234"))
put("phoneNumbers",
jsonArrayNodeOf(
jsonObjectNodeOf("type" to "home",
"number" to "212 555-1234"),
jsonObjectNodeOf("type" to "mobile",
"number" to "123 456-7890")
))
put("children", jsonEmptyArrayNode())
putNull("spouse")
}
Log.d("LOG", root.toPrettyString())