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

Project Explorer and project anatomy

When we create a new Android project, we most often do so with a project template, just as we did in Chapter 1, Getting Started with Android and Kotlin. The template that we use determines the exact selection and contents of the files that Android Studio will generate. While there are big similarities across all projects that are worth noting, seeing the differences can also help. Let's build two different template projects and examine the files, their contents, and how they are all linked together through the code (XML and Kotlin).

The Empty Activity project

The simplest project type with an autogenerated UI is the Empty Activity project. Here, the UI is empty, but it is ready to be added to. It is also possible to generate a project without a UI at all. When we create a project, even with an empty UI, Android Studio autogenerates the Kotlin code to display the UI. Therefore, when we add to it, it is ready to be displayed.

Let's create an Empty Activity project. This is almost the same process as we did in Chapter 1, Getting Started with Android and Kotlin, but with one slight difference that I will point out:

  1. In Android Studio, select File | New | New Project….
  2. On the Choose your project screen, select the Empty Activity template and click on Next.
  3. Change the Name field to Empty Activity App.
  4. Choose the same package name and save location as the previous project.
  5. Be sure to select Kotlin as the language.
  6. Check the Use AndroidX artifacts checkbox as we did previously.
  7. The remaining settings can be left to their default settings, so just click on Next.

Android Studio will generate all the code and other project resources. Now we can see what has been generated and compare it to what we expected in the project explorer window.

If the emulator is not already running, launch it by selecting Tools | AVD Manager and then start your emulator in the Android Virtual Devices window. Run the app on the emulator by clicking on the play button in the quick launch bar:

Take a look at the app and notice how it is a little bit different to that of the first project. It is, well, empty; there is no menu at the top, and no floating button at the bottom. It does, however, still have the Hello World! text:

Note

Don't worry about referring to the first project; we will build another one just like it soon.

Now that we have a brand new Empty Activity App project, let's explore the files and folders that Android Studio has generated for us.

Exploring the Empty Activity project

Now, it is time to go on a deep dive into the files and folders of our app. This will save us lots of time and head-scratching later in the book. Please note, however, that there is no need to memorize where all these files go, and there is even less need to understand the code within the files. In fact, parts of the XML code will remain a mystery at the end of the book, but it will not stop you from designing, coding, and releasing amazing apps.

Take a look at the project explorer window after the project is created:

Notice the two arrows indicated in the previous screenshot. These, as you probably can guess, allow us to expand the app and Gradle Scripts folders.

Note

We do not need to explore the Gradle Scripts folder in the context of this book. Gradle is a significant part of Android Studio, but its role is to hide the quite-complicated processes that Android Studio performs from the user, such as adding resource files, and compiling and building projects. Therefore, we don't need to dig into this any further. If, however, you decide to take Android to the next level, then gaining a good understanding of Gradle and its relationship with Android Studio is time well invested.

We will explore the app folder in more detail. Click on the arrow next to the app folder to expand its contents and we will begin exploring. The first level of contents is displayed in the following screenshot:

We have revealed three more folders: manifests, java, and res. Let's take a closer look in all three, starting at the top.

Note

We will keep our Kotlin code in the java folder. Additionally, since the release of Android Studio version 3.3, there is also a folder named generatedjava , but we don't need to explore it.

The manifests folder

The manifests folder has just one file inside it. Expand the manifests folder and double-click on the AndroidManifest.xml file. Notice that the file has been opened in the editor window and a tab has been added so that we can easily switch between this and other files. The following screenshot shows the new tab that has been added, as well as the XML code contained in the AndroidManifest.xml file within the manifests folder:

We don't need to understand everything in this file, but it is worth pointing out that we will make occasional amendments here, for example, when we need to ask the user for permission to access some features of their device, such as the messaging app or the images folder. We will also edit this file when we want to make a fullscreen app for immersion, such as for a game.

Notice that the structure of the file is very similar to the structure of the layout file that we saw in the previous chapter. For instance, there are clearly denoted sections that start with <section name and end with </section name>. Real examples of this are <application and </application>, and <activity and </activity>.

Indeed, the entire file contents, apart from the first line, are wrapped in <manifest and </manifest>.

In the same way that we enter the brackets of a calculation into a calculator, these opening and closing parts must match or the file will cause an error in our project. Android Studio indents (that is, places tabs) in front of the lines to make the sections and their depth in this structure clearer.

A number of specific parts of this code are worth noting, so I will point out some of the lines.

The following line tells Android that the icon that we want to show the user in their app drawer/home screen, and with which they can launch the app, is contained in the mipmap folder and is called ic_launcher:

android:icon="@mipmap/ic_launcher"

We will verify this for ourselves as we continue our exploration.

The next line has two aspects that are worth discussing. First, it denotes the name that we gave our app; and second, this name is contained as a String with a label of app_name:

android:label="@string/app_name"

Tip

In programming, including Kotlin and XML, a String can be any alphanumeric value. We will learn more about Strings throughout the book, starting in Chapter 7, Kotlin Variables, Operators, and Expressions.

We can, therefore, guess that the alphanumeric value of the label of app_name is Empty Activity App, because that is what we called the app when we created it.

This might sound unusual, but we will see this file shortly along with its label. And, in later projects, we will add more labels and values to it. We will also come to understand the reasons why we add text to our apps in what might, at this stage, seem like a convoluted manner.

We could discuss every line in the AndroidManifest.xml file, but we don't need to. Let's take a look at another two lines as they are related to each other. The following line indicates the name of our Activity, which was auto-generated when we created the project. I have highlighted the Activity name just to make it stand out:

<activity android:name=".MainActivity">

The following line, which appears within the <activity and </activity> tags, denotes that it is an attribute of the activity file. This tells us that this Activity is the one that should run when the app is started; it is the LAUNCHER:

<category android:name="android.intent.category.LAUNCHER" />

This implies that our apps can have more than one Activity. Very often, if you have an app with multiple screens, such as a home screen or settings screen, those screens are built from multiple Activity class instances.

Note

In XML, such as the AndroidManifest file, activity is in lowercase; but in Kotlin, the Activity class has an uppercase A. This is just convention and it is nothing to be concerned about.

As you have just seen, activity in XML has a name attribute with a value that refers to an instance of a Kotlin Activity.

Let's now dig into the java folder.

The java folder

Here, we will find all the Kotlin code. To begin with, this consists of just one file, but as our projects grow further, we will add more. Expand the java folder and you will find three more folders, as shown in the following screenshot:

For this book, we will only need one of these three folders; that is, the top one. The names of these folders are composed of the package name (chosen when we created the app), and the app name, presented in lowercase and with no spaces (this was also chosen when we created the app).

Tip

The reason there is more than one folder with the same name is due to automated testing, which is beyond the scope of this book. Therefore, you can safely ignore the folders that end with (androidTest) and (test).

The only folder that we are interested in for this book is the top folder, which for this app (on my screen) is com.gamecodeschool.emptyactivityapp. Depending on your chosen package name and the name of the app that we are currently working on, the folder name will change, but it will always be the top folder that we need to access and add or edit the contents of.

Expand the com.gamecodeschool.emptyactivityapp (or whatever yours is called) folder now to view its contents. In the following screenshot, you can see that the folder has just one file:

It is the MainActivity.kt file, although the file extension isn't shown in the project window, even though it is in the tab above the editor window. In fact, all the files in the java/packagename.appname folder for this book will have the .kt extension.

If you double-click on the MainActivity.kt file, it will open in the editor window, although we could have just clicked on the MainActivity.kt tab above the editor window. As we add more Kotlin files to our project, knowing where they are kept will be useful.

Examine the MainActivity.kt file and you will see that it is a simplified version of the Kotlin file that we worked with in the first project. It is the same, except that there are fewer functions and less code in the onCreate function. The functions are missing because the UI is simpler, and they are not needed; therefore, Android Studio didn't generate them.

For reference, take a look at the contents of the MainActivity.kt file in the following screenshot:

The file still has the onCreate function, which runs when the app is run, but there is much less code in it, and onCreate is the only function. Take a look at the last line of code in the onCreate function, which we will discuss before moving on to explore the res folder. Here is the line of code under discussion:

setContentView(R.layout.activity_main)

The code is calling a function named setContentView and is passing some data into setContentView for the code in the setContentView function to make use of. The data being passed to setContentView is R.layout.activity.main.

For now, I will just mention that the setContentView function is provided by the Android API and is the function that prepares and displays the UI to the user. So, what exactly is R.layout.activity_main?

Let's find out by exploring the res folder.

The res folder

The res folder is where all the resources go. Left-click to expand the res folder and we will examine what's inside. Here is a screenshot of the top level of folders inside the res folder:

Let's begin with the top of the list; that is, the drawable folder.

The res/drawable folder

The name gives things away a little bit, but the drawable folder holds much more than just graphics. As we progress through this book, we will indeed add graphics to this folder; however, for now, it holds just two files.

These files are ic_launcher_foreground and ic_launcher_background. We will not examine these files because we will never need to alter them, but I will quickly mention what they are.

If you open the files, you will see that they are quite long and technical. They include lists of coordinates, colors, and more. They are what is known as a graphical mask.

They are used by Android to adapt or mask other graphics; in this case, the launcher icon of the app. The files are instructions to Android on how to adapt the app launcher icon.

This system is made available so that different device manufacturers can create their own masks to suit their own Android devices. The masks, which are in the drawable folder by default (ic_launcher_foreground and ic_launcher_background), are default adaptive masks that add visually pleasing shadows and depth to the launcher icon.

Tip

If the concept of adaptive icons is interesting to you, then you can refer to a full and a very visual explanation on the Android developer's website at https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive.

Now that we know enough about drawable, let's move on to layout.

The res/layout folder

Expand the layout folder and you will see our familiar layout file that we edited in the previous chapter. There is less in it this time because we generated an Empty Activity project. It is not entirely empty, as it still holds a ConstraintLayout layout wrapping a TextView widget that says Hello World!.

Be sure to look at the contents – you should find that it looks as you might expect, but it is not the contents that are of interest here. Take a closer look at the name of the file (without the XML file extension): activity_main.

Now think back to the Kotlin code in the MainActivity.kt file. Here is the line of code that sets up the UI; I have highlighted a portion of the code:

setContentView(R.layout.activity_main);

The R.layout.activity_main code is indeed a reference to the activity_main file within the res/layout folder. This is the connection between our Kotlin code and our XML layout/design.

There is a difference in the first project; in the layout folder of the first project, there is an additional file. Later in this chapter, we will build another project using the same template (Basic Activity) that we used in the first chapter to understand why.

Before doing that, let's explore the final two folders and all their subfolders, starting with the next in the list, mipmap.

The res/mipmap folder

The mipmap folder is straightforward – that is, fairly straightforward. Expand the folder to see its contents, as shown in the following screenshot:

Here, you can see two subfolders; they are ic_launcher and ic_launcher_round. The contents of ic_launcher include the graphics for the regular launcher icon we see in the app drawer/home screen of the device, while ic_launcher_round holds the graphics for the devices that use round icons, as opposed to square icons. Double-click on one of the .png files from each folder to have a look. I have photoshopped one of each side by side in this screenshot to aid our discussion:

You are probably also wondering why there are five ic_launcher….png files in each folder. The reason for this is that it is good practice to provide icons that are suitably scaled for different sizes and resolutions of the screen. Providing an image with the hdpi, mdpi, xhdpi, xxhdpi, and xxxhdpi qualifications allows different Android devices to choose the icon that will look best for the user.

Note

The letters dpi stands for dots-per-inch, and the h, m, xh, xxh, and xxxh prefixes stand for high, medium, extra high, extra extra high, and so on. These are known as qualifiers and you will see as you progress throughout this book that Android has lots of qualifiers, which help us to build our apps to suit the wide range of different devices available for users to choose from.

The final conundrum from the mipmap folder is that there is also an XML file in each of the two subfolders. Open one of them up and you will see that they refer to the ic_launcher_foreground and ic_launcher_background files that we looked at in the drawable folder. This tells the Android device where to get the details for the adaptive icons. These files are not required, but they make the icons look better, as well as add flexibility to the appearance.

We have one more folder and all its files to explore, and then we will finally understand the structure of an Android app.

The res/values folder

Open the res/values folder to reveal three files that we will talk about briefly in turn. All these files interlink and refer to each other and other files that we have seen already.

For the sake of completeness, here is a screenshot of the three files in the res/values folder:

The key to understanding is not in memorizing the connections, and certainly not in trying to memorize or even understand the code in the files, but rather to get an appreciation of the interlinked nature of all the files and code we have seen so far.

Let's glance inside the files one at a time.

The colors.xml file

Next, take a look at the contents of the colors.xml file:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>
</resources>

Notice that the starting and closing tags take the usual pattern we have come to expect from XML files. There is an opening <resources> tag and a closing </resources> tag. As children of resources, there are three pairs of <color> … </color> tags.

Within each color tag is contained a name attribute and some curious-looking code consisting of numbers and letters. The name attribute is the name of a color. We will see, in another file that follows, that the various names in this file are referred to from another file.

The code is what defines an actual color itself. Therefore, when the name is referred to, the color defined by the related code is what is produced on the screen.

Note

The code is called a hexadecimal code, because in each position of the code, the values 0 through 9 and the letters a through to f can be used, giving 16 possible values. If you want to find out more about hex colors, visit http://www.color-hex.com/color-wheel/. If you are intrigued about number bases, such as hexadecimal (base 16), binary (base 2), and others, then look at this article, which explains them and discusses why humans typically use base 10: https://betterexplained.com/articles/numbers-and-bases/.

We will see where these names are referred to later.

The strings.xml file

Most modern apps are made for as wide an audience as possible. Furthermore, if the app is of significant size or complexity, then the roles in the software company are often divided up into many different teams. For example, the person writing the Kotlin code for an Android app very possibly had little to do with designing the layout of the UI.

By separating the content of the app from the programming of the app, it is easier to make changes at any time, and it is also possible to create content for multiple different languages without altering the Kotlin code for each.

Take a look at the following contents of the strings.xml file:

<resources>
    <string name="app_name">Empty Activity App</string>
</resources>

You can see that, within the now familiar <resources>…</resources> tags, there is a <string>…</string> tag. Within the string tag, there is an attribute called name with an app_name value and then a further value of Empty Activity App.

Let's look at one more line from the AndroidManifest.xml file that we explored earlier in The manifests folder section. The line in question is displayed in the following code, but refer to the file itself in Android Studio if you want to see the line in its full context:

android:label="@string/app_name"

The android:label attribute is being assigned a value of @string/app_name. In Android, @string refers to all the strings in the strings.xml file. In this specific app, the string attribute with the app_name label has the Empty Activity App value.

Therefore, the line of code in the AndroidManifest.xml file shown previously has the following effect on the screen when the app is running:

While this system might seem convoluted at first, in practice, it separates design and content from coding, which is very efficient to do. If the designers want to change the name of the app, they simply edit the strings.xml file. There is no need to interact with the Kotlin programmers, and, if all the text in an app is provided as string resources, then all of it can be easily altered and adapted as the project proceeds.

Android takes the flexibility further by allowing developers to use different files for string resources for each language and locale. This means that a developer can cater to a planet full of happy users with exactly the same Kotlin code. The Kotlin programmer just needs to refer to the name attribute of a string resource instead of hardcoding the text itself, and then the other departments can design the text content and handle tasks such as translation. We will make an app multilingual in Chapter 18, Localization.

Note

It is possible to hardcode the actual text directly into the Kotlin code, instead of using string resources, and most of the time, we will do so for the sake of easily demonstrating some Kotlin code without getting bogged down with editing or adding to the strings.xml file.

We know enough about strings.xml to move on to the final file that we will explore for the Empty project template.

The styles.xml file

Here, you can see the pieces of the interconnectivity puzzle for this project template finally come together. Study the code in the styles.xml file and we can then discuss it:

<resources>
<!-- Base application theme. -->
<style name="AppTheme" 
parent="Theme.AppCompat.Light.DarkActionBar">
   <!-- Customize your theme here. -->
   <item name="colorPrimary">@color/colorPrimary</item>
   <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
   <item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

This is yet another resource file, but it is referring to the colors.xml file that we saw earlier. Notice that there is a style tag, which is enclosing multiple item tags; each item tag has a name, such as colorPrimary, colorPrimaryDark, or colorAccent. Then, each of these names is assigned a value, such as @color/colorPrimary.

You are probably wondering what is going on; @color refers to the colors.xml file, and colorPrimary, colorPrimaryDark, and colorAccent refer to the actual colors defined with their hexadecimal values in that file. But why bother to create the colors and give them names, and then in another file define item instances and assign those colors to item instances? Why not just assign hexadecimal color values directly to each item?

Take a look at the top of the code block to understand the reason behind this apparently unnecessary convolutedness. I have shown the relevant lines of code again, so that we can discuss them more easily:

<style name="AppTheme" 
parent="Theme.AppCompat.Light.DarkActionBar">

What is going on is that items have been defined and the items are contained within a style element. As you can see, the style is called AppTheme. Furthermore, the style has a parent called Theme.AppCompat.Light.DarkActionBar.

The system allows designers to choose a selection of colors and then define them in the colors.xml file. They can then further build up styles that use those colors in different combinations – there will often be more than one style per app. A style can further be associated with a theme (parent = "…"). This parent theme can be one completely designed by the styles and colors of the app designers, or it can be one of the default themes of Android, such as Theme.AppCompat.Light.DarkActionBar.

The UI designers can then simply refer to a style in the AndroidManifest.xml file, like in this line:

android:theme="@style/AppTheme"

UI designers can then happily tweak the colors and where they are used (items) without interfering with the Kotlin code. This also allows for different styles to be created for different regions of the world without any changes to the actual layout file (in this case, activity_main.xml).

For example, in Western culture, green can represent themes such as nature and correctness; and in many Middle Eastern countries, green represents fertility and is the color associated with Islam. While you might just get away with distributing green in both of these regions, your app will be perceived very differently.

If you then roll your app out in Indonesia, you will find that green is culturally despised among many (although not all) Indonesians. Next, if you launch in China, you will find that green has potential negative connotations to do with unfaithful spouses. It is a minefield that the typical programmer will never learn to navigate. And, fortunately, because of the way we can divide up responsibilities in Android Studio, they don't need to learn.

Therefore, colors, styles, and themes are very specialized topics. While we won't be exploring any more deeply than that quick foray into green, hopefully you can see the benefit of a system that separates responsibility for programming, layout, color, and textual content.

Tip

I thought it is also worth mentioning at this point that images can also be divided up into different locales so that users in different regions see different images within the same app. And, if you are wondering, yes, that will mean supplying different resolutions (such as hdpi and xhdpi, and so on) for each locale as well.

It is also worth mentioning that it is entirely possible to produce a fantastic app that is enjoyed by thousands or even millions of users without catering individually to every region. However, even if we are not going to employ teams of designers, translators, and cultural experts, we must still work within this system that was designed to enable them, and that is why we are going into such depth.

At this stage, we have a good grasp of what goes in an Android project and how it all links together. Let's now build one more app to see the differences that different app templates make to the underlying files that Android Studio generates.