Journey with Kotlin 002 — Dice Roller

Understand how Activity works

Jimmy Liu

--

In this article, I want to talk about how Activity works while building a simply Dice Roller app, that generates a random number between 1…6 whenever the button is pressed.

To begin, let’s set up an app with Empty Activity called Dice Roller.

Next, we are going to take a look at MainActivity.

MainActivity

matryoshka doll

MainActivity is a class that inherited from AppCompatActivity(), which is inherited from FragmentActivity, that inherited from ComponentActivity that inherited from Activity.

A description of AppCompatActivity from Google

AppCompatActivity is a subclass of Activity that supports all modern Android features while providing backward compatibility with older versions of Android. To make your app available to the largest number of devices and users possible, always use AppCompatActivity.

How is an Activity created?

When creating an activity, instead of calling a constructor, life-cycle methods are being called, and onCreate is the first method to be called.

Life-cycle for Activity

As shown in the diagram, an activity has four stages of lifecycle :

  • Activity Launched
  • Activity Running
  • Activity Process Killed
  • Activity Shut Down

During the transaction from one stage to another, we have seven callbacks :

  • onCreate(), onStart(), onResume()
  • onPause(), onStop(), onDestroy()
  • onRestart()

Although you might no need to implement all of the callbacks, but you must at least implement onCreate() to prepare your UI and other required setups.

In the MainActivity’s onCreate , it calls the AppCompatActivity’s onCreate() follows by UI-configuration. XML layout file is specified by passing file’s resource ID R.layout.main_activity to setContentView() , which is an integer reference.

R.layout.activity_main refers to the generated R class, the layout folder, and the activity_main.xml layout file

You can also refer to other resources, such as images, strings and elements within the layout file by using R .

Of course, if you wish you can also insert View within a ViewGroup dynamically. However, you would need to use LayoutInflater, and this will have to wait.

Now that you know what MainActivity does, let’s turn our attention to the activity_main.xml inside res/layout.

Layout XML

As mentioned in the Hello World tutorial, layout xml files are the one that is responsible for your UI. Here is the default setup :

Notice in default, it is using ConstraintLayout as the ViewGroup.

ConstraintLayout allows you to position and size widgets in a flexible way

However, today, we are going to introduce another layout, LinearLayout.

LinearLayout is a layout that arranges other views either horizontally in a single column or vertically in a single row.

There are many other layouts, such as FrameLayout, GridLayout, LinearLayout and RelativeLayout. I am sure we will come over them now and then.

Since we want to use LinearLayout, let’s change the ViewGroup :

It seems things are the same, however, if you look at the preview, you will notice that all the layout_constraint… settings are useless.

LinearLayout vs ConstraintLayout with the same codes

So what should we do ?

First, get rid of all those constraint from the app:.Next, let’s change the android:layout_height to “wrap_content” :

Not sure why there are shadows on both ends

As shown, the preview looks the same, except that the height (blue border) is now limited by the height of the Views within the ViewGroup.

Next, we need to create a Button.

May there be a Button ~~~~

To add a Button on the screen, we simply need to add <Button … /> inside the LinearLayout :

Next, we want to align the two Views vertically. This can be done by setting the orientation of LinearLayout to vertical :

Things are looking great, but it seems we still need to do something with the text.

Time to get the Text right ~~~

This can be done by simply setting “Button” to “Roll”.

Note : In default setting, all the Text in the View are capitalized by default. If you want the text to be exactly what you put, simply add android: textAllCaps = "false" in the View of interest.

In the current setup, every time we wish to change the text in Button, we have to come to activity_main.xml to do the modification. So is there a better way ?

Refractor Values

What we need to do now, is to go to the android:text in Button and pressed ⌥ + Enter (or Alt + Enter). You should see a drop list :

Click on Extract string resource :

  • Resource name : the name or key for this value
  • Resource value : the value for the resource name
  • Source set : Modes you wish to apply this value, you can choose from main, release and debug. Main is the default, it will be available to both release and debug modes.
  • File name : Name of the file you wish to save at. You can choose from colors.xml, strings.xml and styles.xml.
  • Create the source in directories : You must choose on directory to saved at.

After refraction, you should see something like this :

Here, it stated that your text value is a string with the key rollButton.

The at-symbol (@) at the beginning of the string indicates that the XML parser should parse and expand the rest of the ID string and identify it as an ID resource

Also, in your values/strings.xml, you should find your value with that specific key as well :

Now that is done, time to work on the layout for a bit.

Start Laying out

First, let’s make center the ViewGroup by stating the layout_gravity of LinearLayout :

Since we want to ViewGroup to be at the very center of the screen, we simply define android:layout_gravity = "center" .

The reason why the ViewGroup is now perfectly in the center is because we used wrap_content when defining LinearLayout’s layout_height and match_parent for layout_width .

Next, we need to center the TextView and the Button as well. This can be done by defining their android:layout_gravity = "center_horizontal" :

A final touch on the UI is the font or properties of the Views.

Property Adjustments

Obviously, the font of TextView and Button need to be adjusted. This can be done by stating android:textSize=”30sp” in both Text and Button.

When defining android:textSize , you will be prompted with the number with different units:

What are these units?

Pixels based

  • dp : density-independent pixels. This is an abstract unit representing a single pixel on a device with resolution of 160dpi. The view will be scaled based on the dpi of the device. This unit should be used in all layouts.
  • sp : scalable pixels. This is the same as dp, but depends on user’s preference. This should be used when specifying font or text size.

Physical based

  • pt : points, which is 1/72 of the screen size. Generally, not used.
  • px : physical pixel of the device. However, since different devices have different number of pixels and each pixel might have different physical size, this unit should be avoided.
  • in : inches on the device. This unit should also be avoided.
  • mm : millimeter on the device. This unit should also be avoided.

Now that we are done with the UI, let’s take care of the action when the button is pressed.

Button Actions

In order to response to the user when the button is pressed, we need to go back to the MainActivity.kt.

First, we need to get the Button View once the activity is launched :

This is what we’ve got. So what’s wrong?

Apparently, I forgot to set an id for Button and TextView. Just like how we called setContentView with R.layout.activity_main, every time we search for a View, we need to get the id for that View to do so.

Setting ID for Views

All you need to do is to add this line inside the Button block :

Here @+id is used to add a new resource id within R.java. If the id is already existed, then @id would be used instead.

Similarly, we will add android:id = "@+id/dice_textView" for the TextView.

Setting up actions

Now that the button is found :

we need to use OnClickListener to take care of the action when button is clicked.

What is an onClickListener ?

An OnClickListener is an interface that is defined for a callback to be invoked when a view is clicked.

An interface is a group of related methods with empty bodies. When a class is adapts an interface, it needs to implement the bodies of the methods in that interface.

To do so, we called rollButton.setOnClickListener :

What does setOnClickListener does?

Here is what we can tell from the code :

setOnClickListener is a function in View.java file, that takes a parameter with the type OnClickListener as an argument.

Inside setOnClickListener :

  • if the View is not clickable, it will change it to clickable.
  • Then, it will call another function getListenerInfo , which returns a ListenerInfo and set its onClickListener to the intake parameter.

Ok…what is ListenerInfo ?

ListenerInfo is a static class that is defined within View. It is responsible of keeping records of all kinds of Listeners, including OnFocusChangeListener, OnScrollChangeListener, OnClickListener, OnLongClickListener, OnContextClickListener, OnCreateContextMenuListener, OnKeyListener, OnTouchListener, OnHoverListener, OnGenericMotionListener, OnDragListener, OnSystemUiVisibilityChangeListener, OnApplyWindowInsetsListener, OnCapturedPointerListener, WindowInsetAnimationListener, and RenderNode.PositionUpdateListener

We will definitely see more of it in the near future.

Here, you can set the OnClickListener with two methods. You can either simply implement an OnClickListener when setting it, or create a OnClickListener variable and input it as a parameter.

Simply implement OnClickListener
Use OnClickListener variable as a parameter

Inside, let’s make a simple toast, a label that display at the bottom of the screen.

What’s a Toast?

A toast is a view containing a quick little message for the user. The toast class helps you create and show those.

To make a toast message, we usually call Toast.makeText… methods:

These two methods are almost identical, except the second parameter, one uses resId: Int and the other uses text: CharSequence!. Even though they look different, they are very similar. resId:Int is the resource ID of the text you wish to input, while text: CharSequence! is just plain text.

Next, let’s take a look at Context :

Context is an interface to global information about an application environment.

In this case, a context is where the Toast needs to be presented. Since AppCompatActivity is an extends of Activity which is also an extends of Context, we can simply use MainActivity as the context by calling this.

Last but not least, duration, the time before the toast disappears.

Even though duration has a type of Int, but you can only input the two constants, Toast.LENGTH_LONG and Toast.LENGTH_SHORT, provided by the Toast class, otherwise error would occur :

Here, I had made a Toast that shows up in this (MainActivity) context with a message Clicked and with a duration of Toast.LENGTH_LONG.

and here is the result when button is clicked :

Now that we finally get the button to work, time to change the text in the TextView instead.

Changing the Text in TextView

Similar to Button, we first need to define an id for the TextView :

Next, we need to find the TextView at onCreate in MainActivity :

Now, we will simply change the text of the textView when the button is pressed :

And the result :

Finally, we need to generate random number.

Random Number Generator

In Kotlin, a random number generator is created by the Random class :

Random is an abstract class that is implemented by random number generator algorithms.

Random can generate random value for different types, including : Boolean, Bit, Int, Long, Double, Float and Bytes.

Here, we only want a random integer, so here is the code we are interested in :

Don’t mind what is going on inside, it is the algorithm to generate a random number. A random number is generated with different ways, depending on if n, the difference between from and until, is positive or negative.

In order to generate a random number between 1 and 6, we need to create a Random class. So by instinct, we could call this :

A seed is used to create a Pseudorandom number generator. The seed is usually another random value generated by another random number generator.

What if we don’t want to use seed?

Well, we can use Random.Default , a default instance of Random class.

As a result, we can do this :

Are we done ?

No. You see, the method nextInt will generate a random number r that is from ≤ r < until.

Therefore, the two ways to generate random number between 1 and 6 are :

  • Random.Default.nextInt(from: 1, until: 7)
  • Random.Default.nextInt(from: 0, until: 6) + 1

Finally, here is the proper code :

Now you are done !!!!! Congratulations.

Summary

Activities’ lifecycle contains

4 steps :

  • Launched, Running, Process Killed and Shut Down

7 methods :

  • <Launched> → onCreate, onStart, onResume → <Running> → onPause, onStop, onDestroy → <Shut Down>
  • <reactivate at onStop> → onRestart.

If we want to find the View that we are interested, we usually find them inside the onCreate method.

Each View within a layout should be labelled by android:id , this way, you can find the View easily by findViewById . If you wish to add a View dynamically, you can use LayoutInflator. This will be covered in the future.

In this article, I have only showed you one of the way to create an OnClickListener for the button, but there are totally 4 ways to do just that.

If you find this article helpful, please give me a clap. And if you wish to comment or have any questions, please do leave a message, I will be appreciated.

Thank you for your time.

--

--

Jimmy Liu

App Developer who enjoy learning from the ground up.