Hi everyone. My name is Jim Wilson. Welcome to my course, Android Apps with Kotlin: Build Your First App. I'm managing partner of JWHH LLC, and I've had the good fortune to have been a professional software developer now for over 30 years. I've been creating apps for Android since the earliest days of the platform, and I've seen many exciting changes in that time, but none more so than the addition of Kotlin as an Android development language. Kotlin's a game changer. It tremendously simplifies the task of creating Android apps, and it includes a number of features that reduce the likelihood of our apps encountering problems at runtime. Now one of the coolest things about using Kotlin in your Android apps is that you can start using it right away, even in your existing Android apps that have been already been written in Java. Kotlin has fantastic compatibility with Java, and a single Android project can include both code written in Java and Kotlin. And in this course, we're going to teach you everything you need to know to get started developing Android apps with Kotlin. Now some of the major topics we cover include Kotlin language features such as type declarations, null safety, singletons, and data classes; Android activity UI creation and how to connect the UI to your Kotlin code; information passing between activities using intents; activity lifecycle and app data management; and activity instance state management. By the end of this course, you will have successfully created an Android app using Kotlin, and you'll have a solid grounding in the fundamentals of Android app development with Kotlin. This is a beginner course, so you don't need to have any experience with Android or the Kotlin programming language. It is, however, helpful if you have familiarity with any other object-oriented language such as Java or C#. So I hope you'll join me as we learn to develop Android apps with the Pluralsight course, Android Apps with Kotlin: Build Your First App.
Welcome to the Pluralsight course Android Apps with Kotlin: Build Your First App. This is our first module, Creating and Running a Kotlin App with Android Studio. My name is Jim Wilson. Throughout this course, we'll take you through everything you need to understand to start building Android apps using the Kotlin programming language. Now no prior experience with either Android or Kotlin is required for this course. We'll cover everything that you need to know. Now in this first module, we'll cover the basics of creating and running an app using Android Studio. In the next module, we'll take you through the fundamentals of working with types in Kotlin. After that, we'll start getting into the details of creating Android activities and how an activity's code and layout interact. We'll then learn how to launch one activity from another, including how to pass information between those activities. From there, we'll get into options menus and what are called action bar actions. Next, we'll see how Android manages transitions between activities using something known as tasks, and we'll see how to leverage activities from other apps within our own app experience. And then finally, we'll dig into the lifecycle of activities. Now whenever I'm working in a new environment, before I get into all the details, I first like to quickly get a sense of the full end-to-end development experience, and that's what we'll do in this first module. We'll go through the end-to-end experience of creating and running an Android app developed with Kotlin. We'll start out with a short overview of downloading and installing Android Studio. We'll then create a new Android application project that includes support for Kotlin. Next, we'll make some simple changes to the layout of the app's user experience. From there, we'll add some Kotlin code to our app. And then finally, we'll run our app using an Android emulator.
To get started building Android apps using Kotlin, we, of course, will need to have the tools that we'll use. And whether we're working in Kotlin or Java, the primary tool we'll use to create Android apps is Android Studio. Now getting machines set up with Android Studio is super simple. Start out by visiting the URL that I currently have on screen, click on this box here where it says DOWNLOAD ANDROID STUDIO, and when you do, it'll prompt you to accept the terms and conditions. You can then download the Android Studio install file. And once that download completes, simply run the install file and follow the on-screen prompts. Now that installation process will take care of everything you need to get started creating Android apps. It'll provide you with the interactive development environment, including both Java and Kotlin language support. It'll also provide you with the necessary debugging tools, testing tools, and even an Android device emulator. So once the Android Studio install is completed, we'll be ready to start building our Android app using Kotlin. And that's what we'll start doing in our next clip.
Once we have Android Studio installed, we're ready to start creating our Android project. Now the first time we launch Android Studio, you're brought to a screen like this one, and the option that we're interested in is this first one, Start a new Android Studio project. Let's go ahead and select that. So that then takes us to a screen that allows us to identify our project. Now the first thing we'll look at here is the Application name. Now the project we'll be building throughout this course will be a project to create an application that allows us to keep notes about Pluralsight courses. Now we'll talk about the details of the application a little bit later. But for now, let's just go ahead and give it an appropriate name, so we'll name our application NoteKeeper. So that gives us our application name. Now for the Company domain and Project location, we'll go ahead and keep the defaults. But before we leave this screen, there's one key thing we want to do. Notice down here at the bottom there's a checkbox that says Include Kotlin support. Now, by default, Android Studio only includes Java support in the Android projects. Well, we're going to be doing our work in Kotlin, so what we want to do is head down to this checkbox here, and we'll check that box so that our project will include Kotlin support. So now let's go ahead and move to the next screen. So now on this screen, we want to identify what kind of devices we want to target. So we're going to go ahead and keep the default here of Phone and Tablet, so we're building an application so it'll support Android phones and Android tablets, and then we have to specify the API level. So now when it comes to choosing an API level, we're balancing two needs. On one hand, we want to support as many Android devices as possible. On the other hand, we want a version of Android that has as many features as possible. So in order to help us make that decision, Android Studio provides us a link here, Help me choose. And if we select Help me choose, it brings up this screen. Now Android is identified in two ways. The Android operating system is identified by version numbers, and we see those on the left side of our screen here, and those are things like 4. 0, 4. 1, and so forth. But from a developer's standpoint, we look at the API level. And the API level is identified by an integer, and so each Android version supports a particular API level. So, for example, Android 4. 0 supports API level 15, or if we jump further down, Android 7. 0 supports API level 24. So now we look here, we have this column, CUMULATIVE DISTRIBUTION, and that gives us an idea of what percentage of Android devices support a particular version of Android. So, for example, if we targeted Android 4. 1, that means that 99. 2% of devices have Android 4. 1, which is API level 16 or higher. So that means we're going to support lots and lots of devices out there, but we can only use features that are available on API level 16. If we head down here further to where it says 7. 0, API level 24, well that's only 8. 1% of devices. So that would mean our application would work on only 8. 1% of devices out there, but we'd have access to all the features that are available on API level 24. So we're always trying to balance that out, targeting as many devices as possible while getting access to all the features we need. Now this screen helps us know what features were introduced at each API level. So if I go here to Android 4. 4, which is API level 19, and I select that, notice on the right here it gives me a list of the features that were introduced at that API level. If I head down here to Android 7. 0, which is API level 24, and then I select that, it shows me the features that were introduced at API 24. So when we're creating our Android projects, we need to balance these features that are introduced at each API level along with the need to reach as many devices as possible. So let's go ahead and leave this screen. So that takes us back to the screen here where we specify which API level we want to target. Now at the time I'm recording this, Android 8. 0, which is API level 26, supports very few devices. But since we're building this project for our course and we're not really deploying it, we're not really worried about supporting a large number of devices. We really want to see the features that are available, so let's go ahead and stay with that option of using API level 26. So we'll go ahead and go to our next screen. So this screen is where we identify the initial activity we want to include in our application, and we're going to talk a lot about activities throughout this course. Loosely speaking, we can think of an activity as a screen within our application. Actually, it has a lot more going on than that, but for now we'll just think of it as a screen. So let's go ahead and choose the activity option of Basic Activity. So by choosing Basic Activity, we'll have a section at the top of our activity where we can put things like options menus, and our activity will also include something called a floating action button, which is a button that floats above the activity that we can associate tap actions with. So with our Basic Activity selected, let's go ahead and go to our next screen. So on this screen, we'll specify the class name and some other names associated with the initial activity in our application. We'll go ahead and take all the defaults on this one, then we'll head down here to our Finish button, and we'll go ahead and select Finish. And once we do that, Android Studio takes care of generating the project for our application. So now in our next clip, let's take a closer look at our project within Android Studio.
So here we are now in Android Studio looking at our newly generated project. And before we start making modifications to our app itself, let's take a look at some of the relevant parts of Android Studio. If you're on the left-hand side, we have our Project window. The Project window has a tree-like appearance that shows us some of files and folders that are involved in our project. At the top of the Project window is a drop-down selection. Now this drop-down allows us to select the different views of how the project files and folders are displayed within the Project window. Now the view we currently have selected is the Android view, and it's the Project window's Android view that we use the overwhelming majority of the time. So now let's look at the center area of Android Studio. We see two things that look kind of like a screen. Now these are two different representations of our app's activity layout. Now the one to the left is a representation of how the screen might actually look when displayed on a device, and this is what's known as the layout's design surface appearance. Now the one on the right is a representation of the components that make up the layout. This is what's known as our layout's blueprint surface appearance. Now next we have the Palette. Now the Palette is a list of different what we call widgets or views, or sometimes we even call them controls, and we used these within our user interface. Now we can just drag these from the Palette onto either our design surface or our blueprint surface. Under the Palette, we have the Component Tree. The Component Tree just gives us a tree-like representation of how the views are arranged on our layout. Now let's look here down towards the bottom of Android Studio, and you see there we have some tabs. Now one tab is labeled Design. The other is labeled Text. The Design tab is the one we currently have selected, and that indicates that we're currently looking at our layout's design view. So let's select the other tab, which is our Text tab. And when we make that selection, we see some text appear, and you might recognize that text as XML. So what this is showing us is that the layout of our activity is actually described using XML. And by using XML, we're able to describe our activity's appearance independently from the code that interact with it, and this gives us a clear separation between the activity appearance and the associated application logic. Now if we wanted to, we could edit this XML directly. But in general, we don't do that. Instead, we work in the layout's design view and just let Android Studio do the work of generating the XML. So with that in mind, let's go back over here to our Design tab and select that, and that brings us back to our layout's design view. Alright, so now that we have a better understanding of some of the key parts of Android Studio, in our next clip, we'll start working on our application's appearance and behavior.
We're back here in Android Studio, and we're ready to start making some changes to our app's behavior. Before we get started, let's do something that will allow us to see the design appearance of our layout a bit more clearly. What we'll do is we'll hide the Project window. So now we can do that by clicking on the word Project here on the left. So that hides our Project window, and anytime we want to open the Project window back up, we can just click on that word Project again. With our Project window hidden, we now have more room to interact with our layout's design and blueprint surfaces. Now as I mentioned earlier, the project we'll be building throughout this course will ultimately be an app that creates notes for Pluralsight courses. But before we get into that, let's start with something much simpler. Let's do something simple that'll allow us to experience the whole app development experience from layout, to code, to running our app. So for this simple start to our app, what we'll do is take this view known as a text view, so it's currently displaying Hello World!, and we'll change it to display a number. Then we have this floating action button down here, and let's make it so that each time the user taps on that floating action button that we double the number that's displayed in the text view. And while we're at it, we'll have a message slide up from the bottom that displays the number's value before and after being doubled. So for this simple version of our app, the first thing we'll need to do is make some changes to our user interface layout. Now you'll notice here on the right side of Android Studio there's a window labeled Attributes, and currently there's nothing in it. But now if I go over here to the view displaying Hello World! and then I select that, and now the Attribute window shows some of the attributes for the selected view. Now one thing to note is that the Attribute window is currently displaying only those attributes that people most commonly need to interact with. But you'll notice that the Attribute window has a couple of arrows here. If we were to click on those, the Attribute window would then display all of the attributes that are available for the selected view. But for our simple app, the list of attributes that's currently displayed gives us everything we need, so we're all set here. Now one of the most important attributes is the ID. It's the ID that makes the view accessible from within our code. Now when working in Kotlin, each view is automatically accessible from a variable of the same name as a view's ID attribute. So let's set our view's ID to textDisplayedValue. And by doing that, we can now access this view from within our activity's code by using a variable named textDisplayedValue. So now remember, in this simple version of our app, we want to keep doubling the value that's contained within this view. So rather than having this view's initial text set to Hello World!, let's instead make it the number 1. So what we'll do is go down here to our text attribute, and we'll change Hello World! to the number 1. So now that's set, let's go ahead and make a simple cosmetic change to our view. So now to do that, we'll need to scroll the Attribute window down a bit. And once we scroll down, we can see the textAppearance attribute. Now currently, textAppearance is set to Material Small, but then we'll see as we click the drop-down there are a whole bunch of other values that are available. So to make the value within our view a bit more visible, let's change the Material Small to Material Large. So let's start scrolling down, and then we'll just go ahead and choose Material Large. And when we do that, we can now see that our view's font is a bit larger than it was previously. And with that, our layout's all set. So in our next clip, we'll start adding some code to our application.
Here we are back in Android Studio. We're ready to start adding some code to our app. To get to the source code for our activity, we use the Project window, so we'll need to open the Project window back up. To do that, we go over here where it says Project, and we just click on it. Now just as a reminder, we have the Android view of the Project window selected, and remember that the Project window shows folders and files associated with our app. Our top-level folder is the app folder. One of the folders under the app folder is the java folder. Now don't let the fact that the folder's named java throw you. All the source code, whether Java or Kotlin, is under the folder named java. Now let's go ahead and expand that java folder. And when we expand that folder, we see three folders underneath of it. The bottom two folders are where our project's test code is placed. The code for our application is in that first folder. Let's go ahead and expand that first folder. And once we expand that folder, we can see the source code file for our app's MainActivity. So we'll go ahead and double-click on the MainActivity to see that source code. So here we are now looking at the source code for our MainActivity. We'll talk about the details of activity classes a little bit later in this course. For now, let's just focus on adding the code we need to add functionality to our floating action button. So the code we're interested in is here where we have the code fab. setOnClickListener. This code was added for us automatically by Android Studio when we created the project. This is the code that it handles when the user taps on the floating action button. Now the main thing we want to do when the user taps on the floating action button is double the value that's contained in our activity's text view. So we'll go and add a blank line, and then we'll declare a variable to hold the current value of the text view. To do that, we use the val keyword, and we'll name our variable originalValue. Now if this is the first time you've seen a variable declared in Kotlin, that declaration may look a bit odd, but don't worry too much about it because in the next module we'll dig into how we work with types in Kotlin, and as part of that discussion we'll see how variables are declared. So now that we have our variable declared, we can assign it the value that's contained in the view. Now remember when we were working with our layout we set our view's ID to be textDisplayedValue, so that means that here in our Kotlin code we can access that view with a variable named textDisplayedValue. So now to access to view's text value, we use the view's text property. The type that's returned by the text property is something that's known as the a char sequence. Now we want to get our value back as an int. So now to do that, we'll first call the toString method to convert our value from a char sequence to a string, and then we'll call the toInt method to convert the string to an int. So now with that, originalValue will hold our view's current value stored as an integer. So now the next thing we want to do is double that value, so let's declare another variable, and we'll name this variable newValue, and we'll assign it the result of multiplying originalValue by 2. So now we're ready to display the new value. So to do that, we'll start out with the variable for our view, we'll access its text property, we then assign the variable newValue to the text property, and then we'll use the toString method to convert the value from an int to a string. And now that easily our app will double the value displayed in our text view each time the user taps on the floating action button. So now let's do one more fun thing. Let's include a message that slides up from the bottom of the screen and displays the old newValues. Now the way we do that is by using an Android Snackbar. And as you can see, the code generated by Android Studio includes the code necessary to display a Snackbar, so let's just go ahead and modify that code. So the first thing we'll do is change the text currently displayed by the Snackbar. Let's start out by just including the word Value. Now after the word Value, we want to show the view's original value. To do that, we can put a dollar sign followed by the name of our originalValue variable. Now that dollar sign followed by a variable name is known as a string template. So when Kotlin creates that string, it'll take care of the details of getting the variable's current value and including that value in the string. So now the next thing we want to put in the string is the literal text changed to, and then we want to include the value inside of our variable newValue, so again we'll use a dollar sign and then put the variable name newValue after the dollar sign. So let's go ahead and put a line break after the comma so we can see all the code. And then just one last little thing. Let's get rid of this call here to the method setAction. Because our application is simply displaying a message, we don't need to associate any action with that message. So now with that, each time the user taps on our floating action button, we'll double the value that's displayed in our text view, and we'll have a message slide up from the bottom of the screen showing the old and new values. So now in our next clip, let's set up an Android device emulator, and we'll run our code.
Here we are back in Android Studio. We're now ready to run our app. Now we'll run our app using an emulator. And emulators are also known as Android Virtual Devices, or AVDs, but whichever term we use, we're talking about the same thing, a software representation of Android device. Now we'll launch our app using the debugger. And to do that, we'll select this button up here, so then Android Studio will take care of doing a build of our app if that's necessary, and it'll prompt us to select our deployment target. If we had a physical Android device connected to the computer, that would show up in this dialog. Additionally, if we had any existing emulator images configured, they'd show up here as well. But in our case, we don't have any existing images, so let's go ahead and create one. So to do that, we'll go here to this button, Create New Virtual Device, and then we'll go ahead and select that. So now this is the way we create a new emulator image. And to do that, we have to identify the characteristics of that emulator. So the first characteristic we need to identify is the hardware profile we'd like it to use. Generally, we want to use a profile that corresponds to a real device, and to do that, we can select one of the profiles from this list. So let's go up here to Pixel 2, and we'll go ahead and select that. So that indicates that our emulator should emulate a Pixel 2 device. So now we can go ahead and move to the next screen. So now the next thing we need to identify is which version of Android we want installed onto the emulator, so let's go ahead and select Android 8. 0. Now Android 8. 0 has an API level of 26, and its release name is Oreo. Now if you see the word Download next to the selected release name, it simply means that you need to download the files necessary to use that version of Android. You can do the download right from here. All you need to do is click on that word Download. And once you have those files downloaded, it'll bring you right back to this screen. So now that we've selected our Android version, let's go ahead and move to our next screen. Now on this screen, we can make any desired changes to our emulator's configuration. Also, from here, we can rename the emulator image. Now you can name the image anything you'd like, but I generally just go ahead with the default name. The default name is composed of the hardware profile followed by our Android API level. So once we're happy with everything, we can go ahead and click Finish. And when we do, we now see the name of our newly created image listed as an available virtual device. So with that image selected as our deployment target, we'll click OK. And then after a few moments we see our emulator start up, and then it launches our app running within it. Now one thing to be aware of, if you are working with an older computer or a computer with limited processing or memory resources, it may take a few minutes for the emulator to start up and begin running your app. But once the app is up and running, we can go ahead and try it out. So now notice that the value displayed in our app is 1. And as you recall, this is the value we set using the view's text attribute when we were working with the layout in Android Studio. So now we have our floating action button down here, so let's go ahead and select that, and you notice that when we do the value changes from 1 to 2. Notice also that we had a message slider from the bottom. That message was displayed for a few seconds and then slid back down. That was our Snackbar. So let me go ahead and tap the floating action button again, and we can see the message displayed by the Snackbar says that the value 2 changed to 4, and 4 is now the value displayed within our app. So with that, it looks like everything is working just how we like it to. We were able to create our app, we modified its activity layout, we added some code, and now within our emulator we're able to see our app run. So I say that all and all we're off to a fantastic start.
Alright, that wraps up our first module, and how cool was that? In just a matter of a few minutes we created a brand-new Android app, we made some simple changes to its layout, we added some Kotlin code to interact with that layout, and then we ran the app within an Android emulator. So now here are some of the key things you'll want to remember from this module. Now the tool we used was Android Studio, and Android Studio provides a complete development environment for creating our Android apps. Now within Android Studio, we had the layout designer. And the layout designer allows us to add views to the layout to describe our user experience, and we configure the characteristics of those views by setting their attributes. Now the way we access our views in code is based on this ID attribute, so we specify the ID attribute for our view. And when we do that, we'll be able to access that view from within our code using a variable that has the same name as the value of that ID. Basically, Kotlin takes care of synthesizing that variable for us. And then we have our emulator. Now remember that emulator lets us run our Android apps directly on our desktop computer. And also remember that whether someone refers to it as an emulator, an Android Virtual Device, or AVD, in all cases, they're talking about the same thing, a software representation of an Android device. Okay, so now we've successfully created and run an Android app. In our next module, we'll start digging into the basics of working with the Kotlin programming language.
Welcome to our next module, Describing Types with Kotlin. This is part of the Pluralsight course Android Apps with Kotlin: Build Your First App. My name is Jim Wilson. In this module, we'll look at some of the basic concepts around using, defining, and interacting with types when working in Kotlin. Now we're not going to dig deep into any of these concepts. Rather, our focus is on acquiring the fundamental skills required to rapidly get up to speed and start building Android apps using Kotlin. So we'll start out with a quick look of what it's like to use Kotlin on the Android platform, a platform which has historically always been Java based. We'll then take a look at some of the basic types in Kotlin and see how we declare variables when working in Kotlin. We'll see how to define our own types using classes. We'll see how to declare and use properties. We'll learn about something known as a primary constructor. We'll then see how to declare functions and take a look at some of the options we have for passing parameters to functions, and then we'll finish up with a look at type initialization.
Now Kotlin's rapidly becoming the preferred language for developing Android apps. And although there's a lot of reasons that one chooses a development language, here is three of the common reasons that people site as the reason they like using Kotlin to develop their Android applications. So now the first issue is coding efficiency. And with Kotlin, we just tend to write a lot less code than other environments. Now this is due in part due to Kotlin's concise syntax. Kotlin is specifically designed to take advantage of the powerful capabilities of modern compiler technology, and this allows the language to be very expressive while maintaining a very concise syntax. Now also, Kotlin avoids the need to write so much boilerplate code. In other words, Kotlin minimizes the need to explicitly deal with many common housekeeping tasks, and instead it allows us to focus on writing code that's central to our app's features and functionality. So now another key reason that people give for choosing Kotlin is that it helps to reduce runtime errors because the complier is better able to identify potential erroneous situations. Now one way Kotlin does this is with built-in null safety. Kotlin requires that we be very intentional about the nullability of values, and Kotlin enforces rules for interacting with potentially null values. Another key way that Kotlin helps reduce errors is that the language allows us to be expressive about our intentions with a particular value and how we expect that value to be used. Now this in turn allows the compiler to identify when the value is used in a way that's inconsistent with those intentions. So instead of having to wait until runtime to encounter and debug such an error, the potential for the error to occur can instead be discovered by the compiler. And then finally, another key reason people choose Kotlin is for its excellent compatibility, and it has excellent compatibility with Java, and it has excellent compatibility with Android. It's this excellent compatibility that allows us to use Kotlin as an effective solution for developing apps for the Android platform, a platform that's been primarily Java based right from the very start, and Kotlin is fully compatible with Java. Now this allows Kotlin code to take full advantage of Java libraries and all their contained types, and that includes the entire type system used to create Android applications. Now when the Kotlin code is compiled, it's compiled into the same form as code developed in Java. And that means that when we're working in Android Studio, we'd include both Java code and Kotlin code in a single Android project. So if we have an existing app that we've previously created in Java, we can start writing new code in Kotlin, and the Kotlin code will work together with all that existing Java code just fine. Now the main reason we create Android apps is so we can deploy them to our users. And the Android apps we create with Kotlin produce the same distribution file format as Android apps created with Java. So this means we can deploy our Kotlin-based Android apps, and those apps will work fine on our user's Android devices, and even on devices running older versions of Android. And we're able to do this without requiring any special software on the end user's device. The apps that we build using Kotlin are self-contained, and they're just as easy for our user to install and run as an app created using Java. In fact, the user has no indication of whether the app was created with Java or using Kotlin. So now in our next clip, we'll look at the basic types that are available in Kotlin. We'll see how to declare variables that use those types.
Let's look now at some of the basic types in Kotlin. Now, of course, we have signed integers. We can have signed integers that are 8 bits in size, 16, 32, or 64 bits in size, and we declare those using the types byte, short, int, or long. Similarly, we have floating-point types, so we use the float type to declare a floating-point value of 32 bits and the double type to declare a floating-point value of 64 bits. In addition to the numeric basic types, we also have Boolean, which allows us to store a true or false value, char, which allows us to store a single character, and then we have string to store a sequence of characters. Now we'll of course want to declare variables of the various types, and variable declarations are one of the ways that Kotlin allows us to indicate our intentions because there's actually two different ways to declare a variable. One way to declare a variable is using the var keyword, and that declares a mutable variable, and this is what we generally think of as a variable. We can give it an initial value, and we can then later change that value as many times as we would like. Another way we can declare a variable is using the val keyword, and this declares an assign-once variable, in other words, a ready-only variable. So we can give it an initial value, but once a value's been assigned to it, no new values can be assigned to it. So the val keyword allows us to indicate our intentions that we're declaring a variable that once assigned shouldn't change. So let's look at some examples here. So let's declare a variable named student. We'll use the var keyword. Now this is declaring a mutable variable. Now notice that the way we indicate its type is we say var, the variable name, followed by a colon and the type. So student is a string variable. And again, because we used var, it's mutable. So then we can take our student variable, and we can give it an initial value. So in this case, its initial value is the string Jenny Student1. We can go off and do some work with it. And then after that, we can take student, and we can assign it a new value. So now we assign it a new value of Amit Student2, and we can do some work with that. So let's declare another variable. We declare a variable company. We use the val keyword, which means that company is an assign-once variable. Now we've given it the type String, so we can make an assignment to it. So in this case, we're assigning its initial value, so company's initial value is the string Pluralsight. But now remember we indicated we declared company that was an assign-once variable. It's our intention that once this variable is assigned it shouldn't change. So if we have code like company = Another Company, well, since Company's already been assigned and it's an assign-once variable, the compiler will actually indicate that that's an error. So the compiler would catch the fact that we're tying to assign a second value to the variable after we've already assigned its initial value. Now, as we mentioned, one of the values of Kotlin is that it has a concise syntax. So let's look here where we declare our variable. Now we're declaring a variable using the var keyword, so it's a mutable variable, but notice that we haven't given the variable student a type. We simply have assigned it. Now we could have given student an explicit type, but since we're assigning it right here where we declare it, the compiler can infer its type. So since we're assigning a string value to the variable, the compiler will infer that student should be a string. Now, of course, we can go off and do some work with student, and now we can assign a new value to the variable. Alright, so that assigns our new value into it, but now I think it's important to understand that Kotlin is not a dynamically typed language. That variable, student, is a string. So if we were to try to do something like assign an integer to student as the new value, that would actually be a compile time error because Kotlin inferred the variable's type based on the initial value that was assigned to it. Alright, but now as long as we assign a proper value as we're doing here, we could then go off and do some work using the new value in student. And the type of a variable can be inferred whether we're using the var keyword or the val keyword. So we say val company = Pluralsight, again, this is an assign-once variable, and, again, the type is inferred. Since we're assigning a string to company, the compiler will infer that the type of the variable company should be string, but it's still an assign-once variable, right? So if we try to assign a new value to it, again, the compiler will indicate that that's an error. Alright, so now in our next clip, let's see how we can define some of our own types in Kotlin.
As part of building our application, we'll need to define new types. We define types in Kotlin as classes, so we do that using the class keyword, we give our types a name by placing the type's name after the class keyword, and then the body of our class will be contained inside of brackets. There are a number of different things that will make up our classes. Classes can have things like properties, classes can have a primary constructor, and classes can, of course, have functions. And these three things, properties, primary constructor, and functions, those are the things that we most commonly interact with in our classes, but we can also have things like initialization blocks and secondary constructors. We'll look at each of these in more detail as we go throughout this module. So in our next clip, let's start with properties.
Now as we mentioned, classes can have properties, and a property is used to represent a value within a class. Now properties, like variables, must specify the mutability. So if I declare a property using the var keyword, it's a mutable property. If I declare a property using the val keyword, then it's an assign-once property. And when a class has properties, each property could be used to simply store and return a value, or a property can optionally associate code. So we can have explicit code that's run when someone tries to get the value of a property, and we could have a code that's run when someone tries to set the value of a property. So let's go ahead and declare a class. So we have a class. We'll call that class Person. So we start with the class keyword as the name of our class, and then the body of the class goes inside of brackets. So our first property we'll declare with the val keyword. We'll call that property name, its type is String, and we give it an initial value of Jim. Now since this is an assign-once property, it can't be changed because we've already assigned a value to it. Let's go and declare another property. We'll declare this one with the var keyword, and its name is weightLbs. In other words, it's the weight in pounds. Its type is Double, and its initial value is 0. 0. Now because we declare it with the var keyword, it's mutable, which means we can assign new values to it if we want to. So let's go ahead and add another property. This is weightKilos. Again, it's declared with the var keyword. But in this case, rather than having weightKilos directly store values, we're instead going to give it a getter and a setter. So for the getter, we'll associate the code weightLbs divided by 2. 2. So if someone tries to access the value of the property weightKilos, what we'll do is we'll take the value that's stored in weightLbs and convert it to kilos, and we'd do that by dividing the weight in pounds by 2. 2. Then we'll also associate a setter, so our set method takes a parameter named value. So if someone tries to assign a value to this property, weightKilos, that value they're trying to assign is available to us through the parameter named value. So what we'll do in that case is we'll take that value, multiply it by 2. 2, so we'll convert it from kilos to pounds, and then we'll store it in weightLbs. So that gives us our class. Our class has three properties. Now notice that none of these property declarations have semicolons. The same thing for the code with the getter and the code with the setter. And in the previous clip when we were declaring variables we didn't use semicolons at the end of our statements, and that's just something that Kotlin does to help us write more concise code. The compiler is able to figure out when each statement ends, so there's no need for us to end each statement in a semicolon. Let's go ahead and use our class and its properties. So we'll say val p = Person followed by parentheses, so that creates a new instance of the Person class and assigns a reference to it to p. Now notice here that in Kotlin we don't use the new keyword. We create a new instance of a class by simply using the class name followed by parentheses. Again, it's just another thing that Kotlin does to help us write more concise code. So now once we have our reference to that Person instance, we can use it. So here we say val name = p. name, so it takes the value and the name property and assigns it to the local variable called name, so that returns back the string Jim. So now if I say p. weightLbs = 220, well that stores 220 in our weightLbs property. But now if I say val kilos = p. weightKilos, remember that the property weightKilos doesn't actually store a value. Instead, it has a getter. Alright, so that getter took the value that was stored in weightLbs, which is 220, divided it by 2. 2, so it returns back the value 100. So now if I say p. weightKilos = 50, well that'll now run our setter. So that'll take the value 50, multiply it by 2. 2, and store it into weightLbs. So if I then take p. weightLbs and assign it to the variable lbs, it'll then return 110. So it returns back the value that was calculated by the setter associated with our weightKilos property. Alright, so that gives us our initial look at properties. In our next clip, let's take a look at the primary constructor.
Kotlin classes can include a primary constructor. A primary constructor accepts a list of construction parameters. Now the primary constructor appears after the class name and can optionally include the constructor keyword. Now the parameters of the primary constructor are used to initialize the class. Now the thing that's a little bit unusual here is that a primary constructor contains no code. If we want to have code run when we create a new instance of a class, we'll see how to do that a little bit later. So now if we look at the Person class that we were working with in the previous clip, if we want to add a primary constructor to it, let's make a little room after the name of the class, we'll put the constructor keyword, and then we'll have the parameter list. So the primary constructor of the Person class has two parameters, name, which is a String, and weightLbs, which is a Double. Now as we said, the constructor keyword is optional, so we can go ahead and remove that. Now there's no difference in the declaration of class. We still have a primary constructor with two parameters, name and weightLbs. Now we can use these parameters to initialize our class. So if we look at our first property, name, currently it's always initialized to the String, Jim. So we'll replace that String, Jim, with the first parameter of our primary constructor. In the same way, we'll take our weightLbs property, and we'll initialize that with the weightLbs parameter of our primary constructor. Alright, so now those two properties are initialized based on the values that are passed into the primary constructor. But now both properties, name and weightLbs, explicitly have a type associated with them. And just like with a variable, we can actually infer the type based on the value that's assigned to it. So we can remove those so the type of our property name is based on the value assigned to it, which is the primary constructor's parameter name, which is a string. So our name property is also a string. In the same way, our weightLbs property is a double because the value assigned to it is a double. So let's look at some code that uses our class now. So I have val p = Person, and we pass in Bob and 176. 0. Alright, so that creates a new instance of Person. Again, remember that we don't use the new keyword. We simply use the class name followed by parentheses. And since this has a primary constructor, we pass in values to the primary constructor. So the variable p has a reference to that Person instance. So if I say val name = p. name, that'll return back Bob because we passed in Bob as the first parameter of the primary constructor and then initialized our name property. If I say val lbs = p. weightLbs, that returns back 176. 0 because, again, the weightLbs property was initialized with a value passed into our primary constructor. Now, of course, we can still use our other properties. So if I say val kilos = p. weightKilos, that'll still run the weightKilos getter. So we'll take the value in weightLbs, divide it by 2. 2, so it returns 80. 0. As you recall, one of the benefits of Kotlin is it helps to reduce the amount of boilerplate code that we need, in other words, code that we just kind of have to write, the things that don't really add value. Well, let's look at our class here. We have our primary constructor that has a parameter name, and the only thing we're using that parameter for is to initialize our property name in the same way our primary constructor's parameter weightLbs is only used to initialize the property weightLbs. So let's see how Kotlin can improve this situation. Now if we look at our property name and our property weightLbs, what makes those properties is that keyword before them. Alright, name is an assign-once property, so it uses the val keyword. WeightLbs is a mutable property, so it uses the var keyword. So if we look over here at primary constructor, let's make a little room next to each one of those parameters. Well, if we take that val and we put it right before name in the primary constructor, we can get rid of that property declaration. So now that name parameter is also declaring a property called name. If we then go to our weightLbs property, we take the var keyword, put that up in the primary constructor, we could then get rid of this property declaration down here. So now our primary constructor can accept the values, and it declares the properties. But we'll use the class just as we did before. So I want to create a new instance. I can say val p = Person, passing in Bob and 176. 0. That creates a new instance of Person and takes care of initializing the properties. So if I now say val name = p. name, it returns Bob. If I say val lbs = p. weightLbs, it returns 176. 0. Alright, both properties were automatically initialized with the values passed into the primary constructor, but those are full-fledge properties. Alright, so if I say val kilos = p. weightKilos, the getter for that property can still use our weightLbs property. So it takes the value in there, 176. 0, divides it by 2. 2, and therefore returns 80. So declaring the property and the primary constructor doesn't limit the capabilities of the property. It simply reduces the amount of code that we have to type. So in our next clip, let's return to Android Studio, and we'll start declaring some types inside of our application.
Here we are in Android Studio, and what we want to do now is add some classes to our project that we'll use to represent our app's data model. Now just as a reminder, the app we're building will be used to manage notes for Pluralsight courses. So we'll need two classes, one to represent a course and one to represent a note. So the first thing we'll do is head over here to our Project window. We'll confirm that we still have the Project window open in Android view. Then here under the app folder, we have the java folder, which then has a folder for our app's package name. Now remember, although it may seem a bit odd, all of our app's source code is placed within the java folder, even our Kotlin code. So now to get started, we'll go ahead and right-click on the package name, we'll choose New, and then we'll choose Kotlin File/Class. Then here in this dialog that opens, notice that we have a drop-down labeled Kind. Let's go ahead and expand that. Now we have a few options here. Now the kind of obvious option for us is the one called Class. Now if we choose Class, we'll create a Kotlin file with the name we provide, and that file will contain the stub of a class with that same name. But it turns out in our case, I think File might be a better choice. Kotlin, unlike Java, allows us to place multiple classes within a single file, and the Kotlin guidelines encourage us to do so in the case where we have simple classes that are closely related. So we'll go ahead and select File, and we'll name this file NoteKeeperData. Then we'll select OK to create the file. So now we have a relatively empty Kotlin file. We'll add the classes related to our data model to this file. The first class we'll add is a class that represents a Pluralsight course, so we use the class keyword, and we'll name the class CourseInfo. When we create an instance of CourseInfo, we'll want to intitialize it with some characteristics, so our course of a class will have a primary constructor. So to represent our primary constructor, we'll place open and close parentheses after the class name. Now the first characteristic will be the course ID, and the course ID is simply a string value used to identify the course. So we'll name our first primary constructor parameter courseId and give it a type of String. The other characteristic of CourseInfo is the course title, so we'll add another parameter named title of type String. Now we'll want to have both of these characteristics, courseId and title, available as properties on our class, so we can declare them as properties right here in our primary constructor. So now from the standpoint of our app, a course's courseId and title cannot be changed, so both of these properties should be assign-once properties. So for the courseId parameter, we'll use the val keyword to make it an assign-once property, and then we'll do the same thing for title. And that now gives us our complete CourseInfo class. Now notice how simple and clean the class declaration is. On a single line we've given our class name, defined the parameters necessary to create an instance of the class, and exposed two assign-once properties. Also, since our class declaration doesn't need a body, we don't have to include open and closing brackets. Our entire class is declared on a single line. So now let's add a class to represent a note. Now we'll do it right here inside of our NoteKeeperData file just below our CourseInfo class. So we'll declare a class name NoteInfo that has a primary constructor. The first characteristic of a note is the course that's associated with that note, so we'll name our first primary constructor parameter course, and we'll give it a type of CourseInfo. We'll want the note's course to be available as a property, and it's possible that we may want to change which course a note is associated with, so we'll use the var keyword to make course a mutable property. A note will have a title, so we'll add a string parameter named title, and we'll go ahead and add the var keyword to make title a mutable property. And a note will have text, so we'll add a parameter named text of type String, and that's also a mutable property. And that gives us our complete NoteInfo class. And again, this class is declared entirely in a single line. Alright, so now we have the two classes that we'll use to represent the data within our app. In our next clip, we'll start looking at how we can add functions to a class.
Our classes can of course have functions, and the way we declare a function is by using the keyword F-U-N, so it's a keyword fun. Now a function can have a list of parameters, and the parameters can have default values. As part of declaring a function, we can specify the function return type. Now the return type is specified after the parameters. Now in Kotlin, technically, all functions have a return value. But if that value is not really useful, the return type is considered to be the type Unit, U-N-I-T. But in the case of the return type Unit, you can omit the return type in the function declaration if you want to. So let's look at our Person class that we were working on in the earlier clips. Now remember that we have our primary constructor, and it has two properties declared in there, one for name and one for weightLbs. And our class also had a property weightKilos, but I've omitted that declaration on this side just to make what we're doing with our functions a little bit easier to read. So I'm going to declare a function called eatDessert. So I'll use the fun keyword, and I give it the function name eatDessert. Now the body of the function is going to be inside of brackets, and I'll go ahead and give my eatDessert function a parameter list. Now in this case, it has one parameter, addedIceCream, and its type is Boolean. So what I want to do inside the function eatDessert is increase my weight based on whether there was ice cream added to my dessert or not, so I use the if statement there. And if I added ice cream, I'm going to increase my weight by 4 pounds; otherwise, I'll only increase it by 2 pounds. Now in this case, this function doesn't return a meaningful value. The only thing it does is modify the property weightLbs, so the return type of this function is going to be Unit. But now, as we mentioned, if the return type is Unit, we can simply omit the return type. But now in my eatDessert function, if I think about it, most of the times I eat dessert I probably want to have ice cream, so what I'll do is go ahead and give addedIceCream a default value of true. So if no value is passed for the addedIceCream parameter, then its value will be true. Now let's go ahead and add another function to our class. We'll have a function called calcGoalWeightLbs. So, again, we'll use the fun keyword, we're going to have a parameter list, and the parameter list here is going to be the number of pounds I want to lose, and its type is Double. So now in this function what we do is take our current weight in pounds and return back the result by subtracting the number of pounds we want to lose from that current weight. So this function has a meaningful return value, and its return type is Double, so we'll give the function a return type of Double. Now let's go ahead and give the lbsToLose a default value. Because, you know, if you think about it, anytime you ask somebody how much weight they want to lose, so often they'll say well, you know what I want to do, I want to lose 10 pounds, so we'll make the default value of lbsToLose 10. Alright, so in our next clip, let's see some of the options that Kotlin gives us for passing parameter values.
So when our functions are called, our parameters have to have a value. Alright, now that value can be explicitly passed, or if we defined a default value, we can use that default value instead. So let's go ahead and create an instance of our Person class, so we've got that new instance. We have a reference to it in our variable p. And if I say p. eatDessert and I explicitly pass the value false, then the value of the addedIceCream parameter is false, which means that I will increase my weight by 2 pounds. So if I take the value of the weightLbs property and assign it to that local variable lbs, that'll return back 178, the starting weight increased by 2 pounds. But now if I call p. eatDessert and I don't specify a value for the parameter, then addedIceCream will have the default value of true, so that'll increase the weight by 4 pounds. So if I assign the property p. weightLbs to the variable lbs, the weight will be 182, the previous weight of 178 increased by 4 pounds. So now we'll call our calcGoalWeightLbs function, and we're not explicitly passing a parameter, so the lbsToLose will use the default value of 10. So the value returned will be the current weight, 182, with the default value of 10 subtracted from it, so it returns back 172. So now when we pass parameters, we generally think of passing them positionally. So the first value we passed will be the value of the first parameter, second value we pass will be the value of the second parameter, and that carries on. Knowing Kotlin, in addition to passing parameters positionally, we can also pass them by name. So when we make the call to the function, we can include the parameter name along with the value we're passing. And when we include the parameter name, we can pass the values in any order. And when we're using name parameters, we can of course use them in functions, but we can also use them when calling our constructors. So if we look here and we create an instance of Person, so we say val p1 = Person, we pass Jim as the first parameter and 185. 0 as the second parameter, in this case, we're creating the Person specifying the parameters positionally. But we can also pass the parameters by name. So here, val p2 = Person, and we have weightLbs = 185. 0 and name = Jim. So both of these lines create instances of the Person class that have the same name and the same weightLbs. In both cases, the name is Jim, and in both cases, the weight in pounds is 185. 0. Okay, so now in our next clip, let's see how we can have some code execute as part of creating a class instance.
Oftentimes we want to have code within our class automatically run whenever an instance of that class is created. Now the primary way we do that is using something called initializer blocks. And basically, the code we place inside of initializer blocks automatically runs whenever an instance of that class is created. Now there's another mechanism available as well known as secondary constructors. Now a secondary constructor's code is only run when an instance of the class is created using that secondary constructor. But now keep in mind that initializer blocks are the primary way that we have code automatically run when creating an instance of a class because the code inside of an initializer block always runs whenever an instance of the class is constructed. Now you can put multiple initializer blocks within a class, but understand that all of them will always run. Now when it comes to secondary constructors, in the case of a secondary constructor, it only runs when it's used. Now a class can have multiple secondary constructors, and the code within the secondary constructor will only run when an instance of the class is created using that secondary constructor. But it's important to understand that secondary constructors are not an alternative to initializer blocks because any code you place inside of a secondary constructor runs after all of the class's initializer blocks have run. So all of the initializer block code will run, and then the code within the secondary constructor will run afterwards. Also understand that secondary constructors are not an alternative to the primary constructor because secondary constructors must delegate to the primary constructor if one exists. So in Kotlin, these secondary constructors are something you do in addition to the primary constructor and initializer blocks, not so much as an alternative to the primary constructor and initializer blocks. Now we're not going to talk a lot about secondary constructors in this course. I just want to make you aware that they are available as an option. Alright, so in our next clip, let's get back into our Android Studio project, and let's add a class to manage the data within our application. And in that class we'll use an initializer block and some functions.
Here we are back in Android Studio. Earlier we created classes that represent courses and notes within our app. Those were our CourseInfo and NoteInfo classes. What we want to do now is create a class that will serve as the central point of management for instances of those classes. So just as we did earlier, we'll start by heading over to our Project window, we'll confirm that we still have the Project window open in Android view, we'll then right-click on the folder for our app's package name, we'll choose New, and then we'll choose Kotlin File/Class. The class we create to manage our data will be a bit more involved in our CourseInfo and NoteInfo classes. So for Kind, we'll expand that, and then we'll choose Class, and let's name the class DataManager. We'll choose OK to create the class. So now we have our DataManager class. Our DataManager class won't need a primary constructor, but it will need some properties. It'll need a property to hold a collection of courses, and it'll need a property to hold a collection of notes. So let's add a blank line inside the body of DataManager. So now the first property we create will be named courses, and we'll make it an assign-once property. Kotlin includes a variety of collection classes. For our courses, we'll want to use a collection that makes it easy for us to look up courses by the courseId, so let's make our courses property type HashMap. HashMap is a generic type, and generics in Kotlin are used much like generic types in languages like Java or C#. They allow us to specify the specific types we want to use within the type. To specify the type information, we'll place less than and greater than signs after HashMap. Now a HashMap accepts two types parameters. The first is the type that will be used for lookups. We'll be looking up our courses by their courseId, and that's a string, so our first type parameter is String. The second type parameter is a type that will be stored, which for our courses is CourseInfo. That gives us all our type parameters, so we'll go ahead and place parentheses at the end of the line. So now with that in place, it takes care of constructing a new instance of our HashMap, the HashMap maps String values to instances of our CourseInfo class, and a reference of that HashMap is assigned to our courses property. Now let's add a property to hold our collection of notes. Again, this will be an assign-once property, and we'll call it notes. In the case of our notes, we don't need any kind of value-based lookups, so we'll instead use the ArrayList collection. ArrayList is a simple collection that can grow dynamically and provides index-based access to its members. Now ArrayList is a generic type, and the type we want to store is NoteInfo. So we'll pass NoteInfo within the less than, greater than symbols after ArrayList. Then we'll end the line with open and close parentheses. So that takes care of constructing a new instance of ArrayList that can hold NoteInfo references, and we assign the ArrayList reference to our notes property. And that's all the properties our DataManager class needs. So in our next clip, we'll start adding code that will handle the details of initializing our DataManager class.
Here we are again in Android Studio, and we're now ready to start adding code to our DataManager class to initialize it. Now to get us started, let's add a function that will create instances of our CourseInfo class and place them into the HashMap. We'll start by declaring a function named initializeCourses. Now this function doesn't take any parameters, so we'll just follow the name with open and close parentheses, and to hold the body of our function, we'll place some open and close curly brackets. Kotlin, like other object-oriented languages, includes the concept of member visibility. By default, members are public, meaning they can be used from pretty much anywhere. But now in the case of our initializeCourses method, we don't really want anyone outside of our DataManager class calling it, so we'll mark initializeCourses as private. Now we're ready to start doing the work of creating instances of CourseInfo class. So to get us started, we'll add a new line within the body of our function. We'll create a mutable variable named course. We'll create a new instance of CourseInfo. Now as you'll recall, the CourseInfo primary constructor accepts two parameters, one for the courseId and one for the course title. The first parameter is the coursed, so for that parameter we'll pass in the sting android_intents. For the course title, we'll pass in the string Android Programming with Intents. Once we create the course instance, we can add the course to our courses collection. With a HashMap we can do that by using its set method. The first parameter of the set method is the value we'll use to look up the course, and that'll be the course's courseId, so we'll pass in the courseId property of our course. And the second parameter of the set method will be the course itself. So what this will do is allow us to later look at this course by passing in a string with the value android_intents. If we look back here where we created the course instance, you can see that for the two string literals we passed as parameters Android Studio automatically shows the name of the parameter that the literals being passed to. And this is just something that Android Studio automatically does to help avoid errors that sometimes come up due to inadvertently passing the wrong literal to the wrong parameter. Now we can of course explicitly identify which parameter we want to associate with a value by using name parameters. So let's assign the course variable a new CourseInfo instance, and let's use name parameters to create a course as the courseId android_async, and for the tile we'll pass Android Async Programming and Services. And now we'll go ahead and add that course to our courses collection. Now you recall that when using name parameters, the order we list the parameters doesn't matter, so let's create a new CourseInfo instance and assign it to our course variable. And for this course, we'll specify that we'd like the title to be Java Fundamentals: The Java Language. And for the courseId we'll specify java_lang, and we'll go ahead and add that course to our collection. And then we'll add one more course. And for this course, we'll go back to using positional parameters. So that gives us a function that will initialize our courses collection with four courses. So now we need to add the code to have that function run whenever an instance of our DataManager class is created, and we can do that with an initializer block. So just above our initializeCourses function, let's add a couple of blank lines. Now we create an initializer block by using the keyword init followed by open and close curly brackets. So then here within our initializer block we'll go ahead and call our initializeCourses function. Now by doing that, anytime an instance of our DataManager class is created, the code within the initializer block will automatically run, which means we'll automatically populate our collection of courses. Now one thing to note, initializer blocks accept no parameters because there's no way to call them explicitly. They're run automatically as part of instance creation, but initializer blocks can access the parameters of our primary constructor, as well as properties within the class. So in Kotlin, type initialization occurs through a combination of the type's primary constructor, which provides the initialization values, along with the code that runs inside of our initializer blocks. Alright, with that we have our DataManager class in place. So along with the CourseInfo and NoteInfo classes we created earlier, we now have all the necessary types to manage the data for our NoteKeeper app.
To wrap up, here are some of the key things you want to remember from this module. Remember that Kotlin has excellent compatibility with Java and Android. Our Kotlin code can use all our Java libraries directly. When working in Android Studio, a single project can contain both Java code and Kotlin code. And when we deploy our applications, there are no special deployment requirements. The applications we build with Kotlin can run directly on our end-user devices without the user installing any special software. Now in Kotlin, when we declare our variables, we have to declare our intentions for those variables. So if we want a mutable variable, we do that with the var keyword, and that allows us assign an initial value to that variable, as well as change that value later. And alternatively, we can declare our variable as an assign-once variable, and we do that with the val keyword. So once the variable has a value assigned, that value can't change. Now as part of declaring our variables, the type of the variable can be inferred. So when we assign the initial value to the variable as part of the variable's declaration, the compiler will associate the type of the assignment as the type of that variable. Now we can define our own types, and we do that as classes. Our classes can have properties, and properties, just like variable, can be declared as mutable by using the var keyword or as assign-once by using the val keyword. And our properties can simply store and return values, or we can optionally associate code with them by writing our own getters and setters. Remember that our classes can have a primary constructor, and that accepts our type's construction parameters, and those parameters can have default values if we want to. And if the purpose of a construction parameter is to simply initialize a property, we can declare our properties directly in the primary constructor. Our types can of course have functions. Functions are declared with the fun keyword, and remember that our function parameters can have default values. And when it comes to passing parameters, whether to a function or to a constructor, we can pass those parameters positionally, or we can pass the parameters by specifying the parameter name. And remember that our types can have initializer blocks, and initializer blocks are code that runs as part of instance creation. Now our initializer blocks can of course interact with our class properties, but remember they can also access our primary constructor parameters. Alright, that wraps up this module. In our next module, we'll start digging into the details of Android activities, and as part of that, we'll start implementing the first screen of our NoteKeeper application.
Welcome to our next module, Understanding Activities and Activity Layout Interaction. This is the Pluralsight course Android Apps with Kotlin: Build Your First App. My name is Jim Wilson. In this module, we'll look at the cooperation between our activity's code and the activity layout. We'll also dig into the basics of creating an activity's UI. So we'll start out by answering the question what is an activity? We'll then look at what's involved in creating the activity's UI. We'll look at the classes responsible for arranging our activity's UI, classes called layout classes, and we'll pay special attention to one particular layout class called a ConstraintLayout class. We'll then look at the relationship between the code for our activity and the layout used to describe the activity's UI, and then we'll finish up by looking at how we populate a spinner, which requires that we access a view from our layout and populate it with data contained in our application.
If you've been around Android programming for any length of time, you've definitely heard the term activity. In fact, you heard me use it back in an earlier module. So the big question then is what is an activity? And as so often is a good idea, let's start with what the documentation tells us it is. And according to the Android documentation, an activity is a single, focused thing that the user can do. Now that's absolutely an accurate definition, but it doesn't really quite tell us enough. So let's look at a little more detail about exactly what is an activity. Now one of the key things that activities do is they serve as a place to present the user interface. Because activities provide a window, it's within that window that our UI's displayed, and the way we build that UI is by using a series of classes that derive from the base class view. Now it's important to remember though that activities are more than just a place to present a UI. They're actually much more than that. Activities actually have a lifecycle. In other words, activities are kind of a living, breathing thing. Activities have a distinct concept of being created along with a series of lifecycle states that it transitions through. These states have corresponding methods within our activity class. These methods get called as the activity transitions into each state. Now in this particular module, we're going to focus more on the UI aspect of activities, but a little later in this course we'll dig deeper into the activity lifecycle. But one of the key things we need to understand about the lifecycle in order to build our UI is a method called onCreate. The onCreate method is where the lifecycle tells us that our activity is being created. So it's from within our onCreate method that we start initializing our user interface. So in our next clip, let's take a look at exactly what's involved in creating the activity user interface.
As we look at what's involved in building the activity user interface, there are a few things we want to understand. Now one, of course, is the view class. Views are the basic building block of our user interface, and fundamentally, views are responsible for drawing the user interface and dealing with the event handling. Now there are a bunch of specialized view classes. There are view classes that handle text display interaction, there are a variety of button-like view classes, there are view classes for displaying images, as well as many other types of view classes. Now there's a special kind of view called a ViewGroup. A ViewGroup is a view that holds other views. ViewGroup-based classes are also known as container classes. Now we don't do a whole lot of work directly with the ViewGroup class, but there are some classes that derive from it that we work with a lot. A bunch of those fall into a family of what are called layouts. Layouts are special, invisible ViewGroups, and what they're responsible for is dealing with positioning of the child views that are contained within the layout. Layout classes are an important part of activity UI development because activity UIs need to be responsive, and this is because Android supports a very rich set of different devices. And a specific display characteristic of those devices can vary a great deal, so our UIs must adapt to these differing display characteristics. It's important that our UIs not use absolute positioning based on a particular physical screen size because doing that would be far too limiting. Instead, we want to be sure that our user interfaces are adaptive so that our UIs can work effectively across the many different devices that are available, and that's where the layout classes come in. Layout classes provide positioning flexibility because the layout classes are responsible to arrange the child views contained within the layout. The specific positioning behavior will depend on the specific layout class. Layout classes have been a core part of Android UI development from the earliest days of Android, so let's take a quick look at a few of the commonly used layout classes that have been around for a while. Now first, we have FrameLayout. FrameLayout provides a blocked out area, and generally it'll have just one direct child. Now FrameLayout is useful for creating a predicable area of the screen that something else might go into. Now next, we have LinearLayout. LinearLayout provides horizontal or vertical arrangement. In other words, it puts things in line either vertically or horizontally. Now you can have it distribute the child views evenly, or you can have weighted distributions. You can even have some views take up a specific amount of space with other views dividing up the remaining available space. And then there's RelativeLayout. RelativeLayout provides relative positioning, so a view's position could be relative to another view, in other words, to the right of it or below it. Or a view could be relative to the parent, and that would be the RelativeLayout itself. So a view can be positioned relative to the parent's top edge, bottom edge, center, all those sort of things. Now in our next clip, let's take a look at another layout class, and that's the ConstraintLayout class.
The layout classes we looked at in the previous clip have been around since the early days of Android. But as app UIs have become more sophisticated, working with those layout classes can sometimes become challenging because oftentimes we'd have to use a complex grouping of nested layout classes to achieve a desired appearance. These complex nestings made the UIs hard to design and oftentimes resulted in a UI that was slow to render. So to address these challenges, we now have the ConstrainLayout class, and ConstraintLayout is an extremely flexible layout class. ConstrainLayout combines many of the capabilities of other layout classes and, as a result, is often the only layout class needed. ConstrainLayout has a first-class design experience. Unlike earlier layout classes where the layout class itself and the Android Studio design-time experience were developed separately, the ConstrainLayout class and the Android Studio designer support for it were developed side by side. This close integration between the ConstrainLayout class and the Android Studio designer provides us with a rich and easy-to-use design-time experience. Now the ConstraintLayout class, like other layout classes, handles the sizing and positioning of its child views. And as its name implies, the child views of ConstrainLayout rely on constraints. It's these constraints that determine a child view's sizing and positioning. The ConstraintLayout class supports a wide variety of constraints, so we can use constraints to have relative sizing or positioning where a view is sized or positioned relative to another view or the edges of the ConstraintLayout itself. We can have ratio-based sizing and positioning. And using these kind of constraints, we can do things like indicate that a particular view should take up a certain percentage of the width of the screen. We can have weighted relationships where we do things like position an item that's sort of centered, but maybe just slightly to the left or to the right. We have the ability to set guidelines. Guidelines are view elements that are not visible at runtime, but we can use them to indicate anchor points within the layout the other views can then use to determine their sizing or positioning. And then finally, we have a concept known as chains, and chains allow us to group views together and distribute their sizing and positioning across the group. For example, we could use a chain to distribute a series of views evenly across the width of the screen. Views contained within a ConstraintLayout should generally have at least one horizonal and one vertical constraint. If we don't set those, the view ends up positioned at 0, 0. In other words, it's just kind of stuffed up in the corner. Now although we need to have at least one each of a horizonal and vertical constraint, we can have more than one in some cases. For example, a view might have a horizonal constraint on the left side and one on the right side, which then when combined might cause that view to be centered. Now, as I mentioned, the ConstraintLayout is very designer friendly. So when working in the designer, and say we're looking at the blueprint view, the view we're interacting with will actually have this box around it, and that box allows us to interact with it easily within the designer. So if we want to set a constraint within the designer, you'll notice that the box will actually have these circles at the midline. We can then drag from these circles to another point inside the designer to create a constraint relationship. So, for example, if I drag a circle over to the edge of the ConstraintLayout, that relationship is tied to the ConstraintLayout itself. But I also can drag a circle over to another view, and then that would create a constraint relationship between the two views. Now we can also set fixed sizing in the designer, and we do that by using the squares at the corners. So by dragging those squares, we indicate that a view should have a fixed size. Alright, so now in our next clip, let's jump into Android Studio, and we'll use the ConstraintLayout to start creating the user interface for our app.
Here we are in Android Studio, and we're ready to start creating the layout for our activity's user interface. Remember that the app we're building will be used to create notes for Pluralsight courses. As a reminder, let's quickly review the data-related classes we created in the previous module. We'll look first at our NoteInfo class. So now for each note the user creates, we'll need to create an instance of our NoteInfo class. And as you recall, our NoteInfo class has three properties. It has a property for which course the note is associated with, it has a property for the title of the note, and then it has a property containing the note's text itself. Now the note title and note text are simply strings. So from a UI perspective, we'll use a view class that allows the user to enter text. For the course, that'll need to be a reference to an instance of our CourseInfo class, so we'll want to use a view that allows a user to select a course from a list. So with that in mind, let's head over to the layout file for our activity. So now to access the layout file, we'll use the Project window. So our layout file is located under the res folder. Within the res folder, there's a layout folder. Then under our layout folder we have our layout file content_main, so let's go ahead and select that. That opens up our layout file in the designer. Now to give us some more room, let's go ahead and collapse the Project window. So now with our Project window out of the way, let's take a look at our Component Tree. And here in the Component Tree, we can see at the top level we have the ConstraintLayout class. So that tells us that when we're positioning our views we'll be positioning them using constraints. So now within our ConstraintLayout we have this view, textDisplayedValue. And as you recall, that was a view we used back in module one just to do a quick walkthrough of building and deploying our application. But now that we're ready to build our note creation functionality, we don't need this view anymore. So let's go ahead and select it, and then we'll get rid of it by just hitting the Delete key. So now we're ready to start adding the views that we'll need to create a new note. So now the first view we'll add is the view that the user will use to select which course the note is associated with. And remember, they're going to select that course from a list. So now if we head over to our Palette, you can see on the left-hand side I already have the Containers category selected. So then on the right-hand side, I can choose which kind of container I want. The container I want is a spinner. So now a spinner is a form of drop-down list, so it's a great choice for allowing the user to select a course. So let's go ahead and drag the spinner over to our design surface. Now at this point, the spinner appears to be positioned within our layout, but it's really not. The designer is simply giving the spinner the appearance of being positioned so it's easier for us to work with. Because remember, for a view to be positioned within a ConstraintLayout, we have to set constraints on that view. So now in addition to using constraints for the view positioning, we also need to think about whether we want to use constraints for the view's sizing. So let's head over to our Attributes window and take a look at the attribute layout_width, and notice that layout_width has a value of 368dp. There are 2 important points about this value, 368dp. The first is the issue of this thing dp, and dp stands for device independent pixel. And a device independent pixel is a unit of measure that's independent of the actual pixel density of a display. In other words, dps allows us to express sizes in a way that's consistent across devices even though those devices may have very different screen resolutions. In the case of device independent pixels, there are always 160 device independent pixels per inch. And just for reference, an inch is 25. 4 mm. And because of this consistent sizing of dps, they're the preferred way to express fixed sizes when we're laying out the UIs of our Android applications. But now this idea of fixed sizing is another issue for us. We probably don't want to give the spinner a fixed size. We'd rather let the size of the spinner be determined by the constraints we set on it. So to do that, let's expand the layout_width drop-down, and then we'll select match_constraint. And so now that we've done that, the width of our spinner will be determined by the constraints we set. So let's head over to our blueprint surface, and then we'll drag the dot on the right side of the spinner over to the right edge of our ConstraintLayout. And by doing that, we can see that the view is moved over near the right edge of the ConstraintLayout. And then if we look up towards the top of the Attributes view, we can see there's now a number 8 shown on the right edge of that square, and that tells us that that constraint has a spacing of 8dp. So the constraint on the right edge of the view positions the edge of that view 8dp from the edge of the ConstraintLayout. So let's head over to that value 8, we'll select it, and then let's choose a different value. Let's say we choose 32. So now what that does is it sets the constraint spacing to be 32dp, so it moved the view a bit further away from the edge of the ConstraintLayout. So now let's set another constraint on our spinner. Let's drag the left edge of the spinner over to the left edge of the ConstraintLayout. And then over in our Attributes window, let's change that constraint's value from 8 to 32. So now if we look at our design surface, we can see that the spinner is centered with the left and right edges just a short distance from the edges of the screen. And because we set our layout_width to have the value use constraints, it doesn't matter what size display our app runs on. The constraints will assure that the view is always centered, and the width of the spinner will adjust to maintain the spacing from the edge of the screen. So now let's add one more constraint. Let's add a constraint for the top of the spinner. So here in our blueprint surface we'll drag the dot from the top edge of the spinner up to the top edge of our ConstraintLayout. So now over in our Attributes window, let's set the constraint value from 8dp to 16dp. So now we have all the constraints we need set on our spinner. It'll always be positioned 16dp from the top edge of the screen, and the width of the spinner will adjust so that its right and left edges are always 32dp from the edges of the screen. So now we're off to a good start. So in our next clip, we'll add the views for our note text and note title.
Here we are back in Android Studio, and now we're ready to add the views to our layout for the note title and note text. We'll start by adding the view for the note title. So over here in our Palette, we'll select the category for Text. And then from within our Text category, we'll use Multiline Text. And by using Multiline Text, if the user gives a note a long title, the view for that text will automatically wrap to provide multiple lines. This time, just for fun, rather than dragging the view onto the design surface, let's drag it down to the Component Tree, and we'll place it just below our spinner. And when we do that, we see an entry for a view called editText added to be within our ConstraintLayout. So this is the view we'll use for our note title. So now to position the view, we'll head over to our blueprint surface, and then we'll drag the dot from the top edge of our title view down to the bottom edge of the spinner. And now if we look over in the Attribute window, we can see that it has a constraint value of 8. So that means that our note title will be positioned 8dp below the bottom edge of the spinner. Now we need to set the constraints for the right and left edges of our note title. So what we'll do is we'll associate those edges with the corresponding edges of the spinner. So we'll drag the dot from the right edge of the title's view up to the right edge of our spinner, and then we'll do the same for the left edge. And once we do that, notice how the view for the title looks here on our blueprint surface. Rather than the left and right edges matching the edges of the spinner, the title view is actually much narrower than the spinner. And the reason for that is over here at our layout_width attribute. Notice that that attribute currently has a value of wrap_content, and wrap_content indicates that that view should take up whatever width is required to display its content, and that's not what we want. We want the width of the view to be based on the constraints that we've set. So we'll expand the drop-down, and then we'll choose match_constraint. Now that makes the width of our view a bit wider, but the edges still don't match the edges of the spinner, and that's because there's still one more thing we have to address. Let's check out the values of our left and right constraints. And you'll notice here in our Attribute window that they both have a value of 8, and what that tells us is that the edges of the title view have constraints that indent them 8dp from the edges of the spinner. So if we want the edges of our title view to match the edges of the spinner, we need to change those constraint values from 8dp to 0. And once we do that, the edges of our title view and the edges of our spinner now match. So now we need to add one more view to our layout, and that's the view for our note's text. And the view for the note's text should probably be very much like the view we're using for the note's title. It should support multiline text, and the width of the view should match the width of the spinner. So rather than manually create a brand-new view, let's head over to our note title view. We'll right-click, we'll select Copy, then we'll right-click on our blueprint surface, and we'll choose Paste. And once we've done that, if we take a look over in the Component Tree, we can see we now have a view called editText2, and that's the new view that we just pasted onto the blueprint surface. Now when we look at the blueprint surface, we may not see that view because the new view is positioned directly on top of the view we copied. So to help us see a little better, we'll grab that view and just drag it down a bit. And so now we can see that we have the two separate views, but the top constraint of our new view is still based on the spinner. Let's instead set that top constraint to be based on the bottom edge of our note title view. And so now the view for our note text is positioned just below the view for our note title. And since we copied the note title view to create the note text view, the note text view has the same left and right constraints as those of the title view, which means that the constraints for our note text view's left and right edges are tied to the left and right edges of our spinner. So with that, we now have the views within our layout that we'll need to add the functionality to create new notes. So in our next clip, we'll start taking a closer look at the relationship between the layout file and our activity's Kotlin code.
Now as we build our activity and we want to create a user experience, we're obviously going to need to write our Kotlin code to create that experience. It's in the Kotlin source code where we provide the activity's functionality, and that functionality is a key part of our overall user experience. Now that user experience is of course going to tie to the user interface that's displayed within our activity. Now one way we could create that user interface is to do it programmatically. In other words, we could use Kotlin code to create class instances for the views we want to use and then programmatically create all the constraints between those views so that they're arranged appropriately. But in general we don't do that. Instead, we tend to use layout files, and that's exactly what we've been doing throughout this course. The layout files are XML files, and they're separate from our Kotlin source code, and those layout files describe our view hierarchy. Now we create those files using the Android Studio designer. And as part of our app build process, those layout files become resources within our app. But this leads us to an interesting question. If the Kotlin source code for our activity is separate from the layout resource that describes the UI of the activity, how do we create the relationship between our activity's Kotlin code and the layout resource? Well, there's something that's very important to understand. There is no implicit relationship between the source code and the layout resource. An activity must explicitly include code to load the layout resource it wants to use, and it does this using the setContentView method. Now when we call setContentView, we need to identify the specific layout resource we'd like to use. And so to do that, we rely on a class called R. Now R is a generated class that allows us to access app resources. Our layout resource names are contained within a class nested within the R class called layout. To access a specific layout resource, we use R. layout followed by the layout resource's name. So if we want to access a layout resource named activity_main, we would use R. layout. activity_main. Now once our Kotlin code has our activity's layout loaded up, we'll want to interact with views contained within that layout. Now this is a place where Kotlin does a great job of making your life easy. Kotlin takes care of generating synthetic properties to correspond to each view contained within the layout resource loaded our activity. And as you may recall from our first module, the name of the synthetic property is the same as the view's ID attribute. So if when we were working the designer we set a view's ID attribute to have the value's spinner courses, then we could interact with that view from our Kotlin code using a property named spinnerCourses. Alright, so now in our next clip, we'll head back over to Android Studio, and we'll take a closer look at both our activity's Kotlin code and its layout resource.
So here we are back in Android Studio. What we want to do now is make sure that we have everything in place so when we're working in our Kotlin code we can properly interact with the views contained within our layout. Now we're currently looking at the layout content_main. And remember, Kotlin will synthesize properties for us to access the views contained within our layout. Now the name of those properties is derived from the ID attribute of each view. So with that in mind, I want to be sure that each view has a meaningful ID. Now currently, the spinner that we'll be using to choose a course is what we have selected, so a meaningful ID for our spinner might be something like spinnerCourses. And so now when we want to interact with this spinner from our Kotlin code, we'll access the synthesize property spinnerCourses. So now let's go to the view for our note title. Let's go ahead and select that. And for this view, let's give it an ID of textNoteTitle, and so now we can interact with this view using the synthesized property textNoteTitle. While we're working on the view for our note title, let's make one additional change. Now currently, there's nothing within the view that indicates to the user the purpose of this view, so the user won't have any idea what information they're supposed to enter. So here in the Attributes window, let's head down to the hint property. And the hint property allows us to provide text that indicates the purpose of the view, and let's set the hint to Note title. And when we do that, the text Note title appears within the view. So we're all set with Note title, so now we'll go ahead and select a view for our note text. And let's give it an ID of textNoteText, and we'll set the hint to Note text. And with that, all of our views now have meaningful ID values, and for our Note title and Note text fields we also include hint text to help the user. Now let's make another change to our UI layout. We have this floating action button down here towards the bottom of the screen, so let's go ahead and select the floating action button. And you'll notice that I'm not able to select it. In fact, if we look over here in the Component Tree, the floating action button isn't even listed in the Component Tree. The only items there are the ConstraintLayout and the three views that we added. So where is this floating action button coming from? Well, let's head over to the tab for our Project window, and we'll go ahead and expand the Project window. We can see here in our Project window the content_main layout, but notice just above it there's another layout named activity_main. So let's go ahead and select that. And now if we look at the design surface, the appearance of our activity_main layout looks very much like the appearance of our content_main layout. But for this layout, if we look at the Component Tree, we can see that it includes the FloatingActionButton. So now if we go down here to the FloatingActionButton and we select it, we can see that the ID of this FloatingActionButton is fab, or F-A-B. So again, if we want to interact with this floating action button in our code, we would use the synthetic property F-A-B. Now let's look again at the Component Tree, and notice that the Component Tree contains an include entry. And if I hover my mouse over it, we can see that it's actually including our other layout, content_main. So basically, the layout we're currently looking at, activity_main, has the basic structure of our screen, and then it includes another layout, content_main, that has the layout detail that we're working in. Now, as you recall, we want to get rid of this floating action button. So since we already have it selected, I can delete it by just hitting the Delete key. And so now that's gone, so our layout files are all set. So let's head up to our source code file, MainActivity, and we'll open that up. So this is the Kotlin class for our activity. And if we look here at its onCreate method, you'll notice that inside there we see a call to setContentView. And remember, it's by calling setContentView that we indicate what layout resource we want an activity to display. And you can see that the layout we're using is R. layout. activity_main, and that was the layout we were just working in. So this activity will display the content from our activity_main layout, as well as any layouts it includes such as our content_main layout. Now notice also in the onCreate method we have some code that's highlighted in red. We see that fab is displayed in red, and that's a synthetic property that corresponds to the floating action button we just deleted. And we also have textDisplayedValue appearing in red, and that's a synthetic property for the view that we deleted earlier in this module. So as we can see, Kotlin here within Android Studio is managing all the synthetic properties for the views within our layouts. Not only does it create the properties for us when we add views to our layouts, but it also gets rid of the properties for us when we remove those views. So since both fab and textDisplayedValue are no longer part of our layouts, let's take all this code here that uses those properties, and we'll delete it. So now let's just go ahead and do a quick build to make sure that everything is as it should be. So we'll head up to our Build menu, we'll select Build, and we'll choose Make Project. And so with that, we can see our project builds just fine. So we're now all set with our activity layout, and all of our Kotlin code is cleaned up. So now in our next clip, let's see how we can use something called an adapter to populate the content of our spinner.
When we created our app's activity layout, we used a view known as a spinner. Now the role of a spinner is to present the user with a drop-down list of options that the user can select from. Now a spinner has two identities. It has one part that shows the current selection, and then when the user taps on the triangle an area opens up to show the available selections. What that means then is that somewhere in our program there needs to be some data, and we'll use that data to populate the selections within the spinner. It turns out though that there's actually even a bit more to a spinner. Spinners also need to have layouts associated with them. We have to have one layout to format the current selection, and then another layout is used to format each of the available selections. So that means there are three tasks involved in populating a spinner. There's retrieving the data and then managing the two layouts. And that's where adapter classes come in. Adapters are responsible for doing the work of retrieving the data and managing each of those layouts. Now there are different kinds of adapters available. Some adapters manage in-memory data sources, like a raise or list. Others manage cursor-based