Android Programming with Kotlin for Beginners
上QQ阅读APP看书,第一时间看更新

Adding buttons to the main layout file

Here, we will add a couple of buttons to the screen and will then explore a quick way to make them do something. We will add a button in two different ways; first, using the visual designer, and second, by adding to and editing the XML code directly.

Adding a button via the visual designer

To get started adding our first button, switch back to the design view by clicking on the Design tab underneath the XML code that we have just been discussing. The button is highlighted in the following screenshot:

Notice that in the left-hand side of the layout, we have a window that is called Palette:

The palette window is divided into two parts. The left-hand list has the categories of the UI elements and allows you to select a category, while the right-hand side shows you all the available UI elements from the currently selected category.

Make sure that the Common category is selected, as shown in the previous screenshot. Now, left-click and hold on the Button widget, and then drag it onto the layout somewhere near the top and center.

It doesn't matter if it is not exact; however, it is good to practice to get it right. So, if you are not happy with the position of your button, then you can left-click on it to select it on the layout and then tap the Delete key on the keyboard to get rid of it. Now you can repeat the previous step until you have one neatly placed button that you are happy with, as demonstrated in the following diagram:

At this point, we can run the app on the emulator or on a real device, and the button will be there. If we click on it, there will even be a simple animation to represent the button being pressed and released. Feel free to try this now if you like.

To start making the app more interesting, we are going to edit the attributes of our button using the Attributes window.

Editing the button's attributes

Make sure the button is selected by left-clicking on it. Now find the Attributes window to the right of the editing window, as follows:

In the preceding screenshot, you can see that we have access to some, although not all, of the button's attributes. To reveal more of the attributes, click on the View all attributes link (indicated in the preceding screenshot).

Now you can see the full details of the button and we can set about editing it. It might seem surprising to see the substantial number of attributes that something as apparently simple as a button has. This is a sign of the versatility and power that the Android API provides for UI manipulation. Look at the following screenshot that shows the full attributes list for our recently added button:

Furthermore, notice that even the previous screenshot doesn't show everything, and you can use the scroll bar on the right of the Attributes window to reveal even more attributes.

As you can see, there is a large array of different attributes that we can edit right here in the UI designer. In Chapter 12, Connecting our Kotlin to the UI and Nullability, we will also edit and manipulate these attributes using our Kotlin code. For now, we will edit just one attribute. Scroll down the Attributes window until you see the onClick attribute and then left-click on it to select it for editing, as shown in the following screenshot:

Tip

The attributes are in alphabetical order and onClick can be found about two thirds of the way down the lengthy list.

Type topClick in the onClick attribute's edit box and press Enter on the keyboard. Be sure to use the same case, including the slightly counterintuitive lowercase t and upper-case C.

The Attributes window will look like the next screenshot when you are done:

What we have done here is named the Kotlin function in our code that we want to call (or execute) when this button is clicked on by the user. The name is arbitrary but, as this button is on the top part of the screen, the name seems meaningful and easy to remember. The odd casing that we used is a convention that will help us to keep our code clear and easy to read. We will see the benefits of this as our code gets longer and more complicated.

Of course, the topClick function doesn't exist yet. Android Studio is very helpful, but there are some things that we need to do for ourselves. We will write this function using Kotlin code after we have added a second button to our UI. You can run the app at this point, and it will still work, but if you click on the button, the app will crash and you will get an error message, because the function does not exist.

Examining the XML code for the new button

Before we add our final button for this project, click on the Text tab underneath the editor to switch back to seeing the XML code that makes our UI:

Notice that there is a new block of code amongst the XML code that we examined earlier. Here is a screenshot of the new block of code:

Notice the following details, which should correspond to what we know about XML and Android UI elements:

  • The new code starts with the <Button text and ends with />.
  • The new code has a range of attributes that define the button, including layoutWidth and layoutHeight.
  • The code includes the onClick attribute that we added with a value of "topClick".
  • The topClick value of the onClick attribute is underlined in red, showing an error because the function does not exist yet.
  • The start and end of the code representing the button is enclosed within the ConstraintLayout element.

Note

The dp is a unit of measurement/distance and will be discussed in more depth in Chapter 5, Beautiful Layouts with CardViewand ScrollView.

Hover the mouse cursor over the underlined topClick value to reveal the details of the problem, as shown in the following screenshot:

We can confirm that the issue is that Android Studio expects a function called topClick to be implemented within our code. We will do this as soon as we have added that second button.

Adding a button by editing the XML code

Just for variety, and to prove that we can, we will now add another button using only the XML code, and not the UI designer. Most of the time, we will use the UI designer, but this quick exercise should cement the relationship between the UI designer and the underlying XML code in your mind.

We will achieve this by copying and pasting the code for the existing button. We will then make some minor edits to the pasted code.

Left-click just before the button code that starts <Button. Notice that the beginning and end of the code now has a slight highlight:

This has identified the part of the code that we want to copy. Now, left-click and drag to select all the button code, including the highlighted start and end, as shown in the next screenshot:

Press the Ctrl + C keyboard combination to copy the highlighted text. Place the cursor below the existing button code and tap the Enter key a few times to leave some additional empty lines.

Press the Ctrl + V keyboard combination to paste the button code. At this point, we have two buttons; however, there are a couple of problems:

We have an additional error in both blocks of code that represent our buttons. The id attribute (in both blocks) is underlined in red. The reason for this error is that both buttons have an id attribute that is the same. The id attribute is supposed to distinguish a UI element from all other UI elements, so they must not be the same. Let's try and fix that.

Giving the buttons unique id attributes

We could solve the problem by calling the second button as button2, but it will be more meaningful to change them both. Edit the code in the first button to give it an id attribute of buttonTop. To do so, identify this following line of code (in the first button):

android:id="@+id/button"

Then, change the line of code to the following:

android:id="@+id/buttonTop"

Tip

Notice the lowercase b in button and the uppercase T in Top.

Now identify this line of code in the second button:

android:id="@+id/button"

Then, change the line of code to the following:

android:id="@+id/buttonBottom"

The errors on the id attribute lines are gone. At this point, you might think that we can move on to solving our missing function problem.

However, if you run the app and take a quick glance at it, you will see that we only appear to have one button. Not only that, but the buttons are not in the places that we expected them to be in, either:

The reason for this is that we haven't explicitly positioned them, so they have defaulted to the top-left of the screen. The position we see on the Design tab is just a design-time position. So, let's change that now.

Positioning the two buttons in the layout

The reason we can only see one button is that both buttons are in the same position. The second button is exactly covering the first button. So, even in the Design tab (feel free to take a look), the buttons are still sat on top of each other, although they are in the middle of the screen:

Note

You might be wondering why the UI layout tool was designed in this apparently counterintuitive way; the reason is flexibility. As you will see in the next two chapters, not only is it possible to position UI elements differently at design time to when the app is running, but there is also a range of different layout schemes that the app designer (that's you) can choose from to suit their plans. This flexibility results in a little awkwardness while learning about Android, and great design power once you have got past this awkwardness. But don't worry, we will move a step at a time until you have this thing beaten.

We will make Android Studio solve the problem for us automatically by first adding to our code, and then using the UI designer. First, let's get the design time layout right. In the code for the second button, locate this line of code:

tools:layout_editor_absoluteY="30dp" />

Now edit it to be the same as the following line of code:

tools:layout_editor_absoluteY="100dp" />

Tip

Depending upon exactly where you positioned your first button, the values in Android Studio will likely be different to the values just discussed. If the second button is around 70dp higher than the first button, then you can proceed with this exercise.

This subtle change will move the second button down a little, but only for design time. If you look in the Design tab, the button is positioned neatly underneath the first button, but if you run the app on the emulator, they are both still in the top-left corner of the screen and are on top of one another.

Switch to the Design tab and find the Infer Constraints button that is shown in the following screenshot:

Click on the Infer Constraints button. Android Studio will edit the XML code. Let's take a brief look at what has happened behind the scenes. From the end of both of the sections of code representing the buttons, the following lines of code were removed:

tools:layout_editor_absoluteX="147dp"
tools:layout_editor_absoluteY="30dp" />

These two lines of code were what positioned the buttons horizontally (…absoluteX) and vertically (…absoluteY).

Android Studio also added four lines of code to the first button, and three lines of code to the second button. Here is the code that was added near the start of the first button:

android:layout_marginTop="30dp"

Tip

Exact values of dp might vary depending on precisely where you placed your buttons.

This code causes the button to have a margin of 30 on the top. But on the top of what exactly? Look at the following three lines of code that were added at the end of the first button:

app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

Notice the new attributes of layout_constraintEnd_toEndOf, layout_constraintStart_toStartOf, and layout_constraintTop_toTopOf. The value that is assigned to each of these attributes is "parent". This causes the first button to be positioned relative to the parent UI element. The parent is the layout that contains everything else; in this case, the parent is the ConstraintLayout element.

Now look at the three lines of code added to the second (bottom) button.

Near the start of the code, we see the following:

android:layout_marginTop="22dp"

Tip

Again, exact values of dp might vary depending on precisely where you placed your buttons.

At the end of the code for the second button, we see the following two extra lines of code:

app:layout_constraintStart_toStartOf="@+id/buttonTop"
app:layout_constraintTop_toBottomOf="@+id/buttonTop" />

This means that the second button is positioned with a margin of 22 relative to buttonTop.

Now run the app and you will see that we have two distinct buttons. One has an id attribute of buttonTop, and it is above the other button with an id attribute of buttonBottom:

Clearly, there is more to layouts than I have alluded to so far, but you have had your first glance at the options provided by Android Studio to design the UI of our apps. We will be taking a closer look at ConstraintLayout, as well as exploring more layout options in Chapter 4, Getting Started with Layouts and Material Design.

We want to make one more alteration to our XML code.

Making the buttons call different functions

Switch back to the Text tab and identify this next line of code in the second (buttonBottom) button:

android:onClick="topClick"

Next, edit the code as follows:

android:onClick="bottomClick"

Now we have two buttons, one above the other. The top one has an id attribute of buttonTop and an onClick attribute with a value of topClick. The other has an id attribute of buttonBottom and an onClick attribute with a value of bottomClick.

These last XML code changes now mean we need to supply two functions (topClick and bottomClick) in our Kotlin code.

Tip

It is technically OK for two buttons to call the same function when they are clicked on – it is not a syntax error. However, most buttons do have distinct purposes, so this exercise will be more meaningful if our buttons do different things.

We will do that soon, but before we do, let's learn a little bit more about Kotlin comments and look at some Kotlin code that we can write to send messages to the user, and to ourselves for debugging purposes.