1. Home
  2. Training Library
  3. Programming
  4. Programming Courses
  5. Layouts and Animation for Android Apps

Challenge Exercise Solution

Start course
Overview
Difficulty
Intermediate
Duration
4h 7m
Students
1
Description

In this course, you'll start experimenting with XML code and diving deeper into layouts, namely linear and constraint layouts. We'll also look at animations and build a few fun features with them. Then, we'll take a deeper dive into the Kotlin programming language and constraint layouts, before building a fully functioning Tic-Tac-Toe game.

On top of that, we'll then build a second app which can play YouTube videos within it, and you'll learn how to work with APIs and API keys.

Intended Audience

This course is intended for beginners to Android app development or anyone who wants to master coding in Kotlin.

Prerequisites

Since this is a beginner-level course, there are no requirements, but any previous experience with coding would be beneficial.

Transcript

Hello, and welcome back, hope you managed to implement one of the two challenge exercises and determine and display who won in the Toast message. If you managed the second one of resetting the game, then that's amazing because that was a very tough challenge, especially, so early on in your Android and Kotlin journey and would have required a fair amount of googling and taking a couple of leaps of faith. So, well done if you managed it, but if you didn't don't worry about it at all, it was very tough. So, let's start with the easy one which is notifying the user which player has won, either circles or crosses. You see here crosses have won, so in this scenario rather than just saying that we have a winner, which we're doing, we want to actually display who it is that won. 

And if you look at the code, at this point, we either have active player as 1 or 0. If it was active player 0 and an x fell into the slot which caused a winner like this, then the active player you see would have switched. Because after the x fell into place, the active player would have turned into 1, which is a circle. In another scenario, if circles had 1, then here a circle would have dropped, would have determined the winner. So, circles would have technically won, but the active player would have been reset to 0, which is a cross. So, from here you can see that whoever won is no longer the active player. So, the opposite of the active player is the winner in this case, and that's what we are checking here to see who won. 

So, if we have a winning scenario, we want to set the winner here, and it would be the opposite of the active player. So, let's start it right here. On top of the Toast, we'll first use var winner because we want to set it and we'll set it as an empty string and then the logic would go something like this, and we're going to come up with a cleaner way of doing it, but first, let's break it out. So, if (activePlayer) at this point =1, then we know that crosses have 1 because remember it's the opposite, so what we'll do is say over here, winner = "crosses". And here else circles have 1, so we'll say winner = "circles". And now, that we have this winner variable what we can do is over here, instead of we have a winner, we can say string winner, which is our variable, or $winner have won, so either circles have won or crosses have won. Let's test this out and make sure it works. 

Let's apply changes and rerun. So let's test crosses first. So, it is cross circle cross circle cross, there you go, crosses have won, great. Now, let's test circles. So I'll rerun it, cross circle cross circle cross, here we go, circles circles have won. Perfect. All right. So, when you have logic like this, If (active player == 1), it's one condition and an else and you're using a winner variable like this, you can actually compress code like this. For example, var winner =, we can bring our if condition up here. So, instead of var actually I'm going to use val because our winner is staying the same and we're not going to initialize it as an empty string. So, what we'll say is if (active player = 1), so whatever we were doing here, we're simply going to check that here. Then I only want to set the value of winner to "crosses" and that's how I do it over here, and that will take care of this first part, So,

code like this you can compress to this and then what do we do about the else? You simply put else "circles" right up here. So, this code is equivalent to having set a winner to empty and then going through this entire block you can compress it to simply this one line if (active players == 1), then "crosses" else "circles", and that will get assigned to winner right here, then we can get rid of this entire if else block, there we go. This will give us the same result. If I apply and run it again. Let's make circles win circle, then cross here, circle and then cross here circle. There we go, circles have won, still working perfectly, great. And now that we are able to let the user know which player has won, let's move on to the challenging bit of resetting the game so users can play again. I'll pull up my layout over here. 

Let's start with a button on top here that says Play Again, so when tapped it would reset the game. I'm going to go to Design, let's drag in a Button, drag one here. Let's quickly try to set some constraints and see what happens left, on top, and then to the right and you can see it's all over the place so let's fix it. I'll bring my split view in and I'll copy the Button from the bottom over here command X, I'll put it on top. Since I have it towards the top anyway, so it makes sense to have it here. Now, let's fix it layout_constraintEnd_toEndOf, so the end, I want it to the end of the imageView which I have over here, which is the board. So, instead of parent, I'm going to set it to imageView right there, so it's constrained to the right and then constraints start to the end of I don't really need this, I'm going to do start_toStartOf imageView as well.

 So, I'll get rid of this. I'll start typing start, there you go, start_toStartOf and this will be imageView as well. There you go, better placement and now top_toTopOf="parent", which is my constraint layout that works as well but since I'm using imageView here, I'll constrain it to the imageView as well. So, I'll start saying imageView, there we go. Now, let's set these. So, margin start, I'll set to 32 and then margin top, I'll set to 32 as well, so it pushes it down a little bit. And then I'm going to introduce a margin to the right so that's the marginEnd  and I'll set it to 32 DP as well. Perfect so the button is placed, next I'll work on the text of the Button. The text, I'll set to, Play again?, so "Play again?" there you go. Now, the next thing is see the button is showing up regardless of what state of the game I'm in. So, if I ran this right now, you see the button has popped up over here, I don't want it to display at first, I only want this to show up once I have a winner. So, let's say once this happens and we have a winner, then I want the button to show up not in the beginning, so how do we do that? First, we set this button to be invisible and then once we have a winner we set it to visible. So, how do we set it to invisible in the beginning? 

Well, we can set the visibility. So, we'll say, if I start typing visibility, you see android:visibility attribute pops up and I'm going to set it to invisible to start and you see the button goes away and then once we have a winner, I want this back on. So, in my code here I have a winner, so once I set the game active status to false, I'll grab that button. So, I'll say val and I'll call it playAgainButton and I'll set it to the button. So, binding. and there is my button and the id is showing up as button2, let's just make sure button id yeah it's button2. So, now that I have the playAgainButton, I want to set it to visible. And the way to do that is you refer to playAgainButton. and you see all these attributes pop up, there's the visibility. So, if you start typing in visibility, you'll see visibility show up and we want this to be set to visible. So, visibility is a property of the View class and you could have checked for this by googling android view set visibility for anything but here I'll just show you how to do it. 

There we go, View is the android View class that I have dot and I'll set you see invisible, visible, all of this. If you start typing in visible with a V, you see visible pops up, there we go. And that's how you make the button visible. So, let's run it, make sure it works. All right, you see the app shows up and the button isn't there, let's have a winner and crosses have won and there's the PLAY AGAIN button. And, of course, if you click on it right now, nothing happens because we haven't set that up yet, so let's do that which is next. So, back to our activity main. For the button, we're going to set up an onClick attribute. So, right here we'll start saying onClick and, there you go, it pops up and we will call our method resetGame because we want the game to be reset once that button is tapped and it's red right now because that method doesn't exist. So, let's go ahead and set it up. 

There is our drop in, it closes here. So, I'll say fun, I'll make some more room so you can see better, resetGame. It's going to take in (view: View) {} and now we can get started with our method. So, what are the tasks that we want our method to accomplish? Let's set them up one by one. First, we want to make the button invisible again, right? Once I tap on 'Play again', I want the game to be reset and I want the button to go away because we're starting off a new game. So, down here, what we'll do is simply set the visibility to be invisible. So, we'll grab the button, binding.button2 and then we'll set the visibility. Once I start typing visibility, it shows up to be View.INVISIBLE. There we go. So, now the button is going to be invisible once this method kicks off. Now the second thing we want our method to accomplish is we want to reset our game state array that we have, right? As we are playing, the game state array that we have over here is getting manipulated with 0s, 1s, and all of that good stuff. 

So, what we want is all of our slots to be updated to be two like this. And we can do that using a loop. So, down here, we'll say four, let's come up with a variable name, let's say tappedslot. And we've used this variable name of tappedslot in another method up here. You see in this method dropIn, we have tappedslot but that variable is local to that method dropIn, okay? So, right here when we say tappedSlot, this variable is not referring to the same variable. And even within this method, this variable is going to be for this for loop, but that's a different story. All right, so for each tappedSlot in, let's come up with a range of 0-8. So, that means we're going to get tappedSlot to be from zero through 8 which will fit our indices nicely over here, 0,1,2,3 all the way through the last one which is 8. And over here, what we want to do is simply refer to game state at that tappedSlot. 

So, game state, 0, 1, 2, whatever all of them to be two, all right. And when you have this, you can actually do this in one line, so I'll simply move this up, there we go. And this will reset our game state array to be all twos. And once that happens, would want our new game to start, right? So, we want our players to be able to tap on the slots again. So, we want to set the gameActive = true. Our game active once we have a result is getting set to false over here, indicating that the game is no longer active, so we want to set that to true. So, let's do that, game active we'll set it to true. And then the next step would be to remove all the images of the circles and crosses that are in the layout showing up. But we've written a few lines of code already. Let's first test this out and make sure it works, and then get to that last bit. So, let's run, make sure it works. I'll actually pull up my log cat as well so we can track the game state. 

Okay, I'll move this over here. All right, so there you go, cross. There, we have a winner crosses of one. There's the button that's popping up, there's our game state array in its altered form. Now, once I hit this 'Play again' button, I'm expecting this to disappear. All right, that's gone, which means I should be able to tap on one of these empty slots again. So, if I do that, there you go, you see all of them or twos. And of course since I tapped on this, it changed to one, but you can see that the game state array had changed to all twos, all right, great. So, now this last bit, we'd want to remove all of the circles and crosses from the existing board layout, so we have an empty board to start. And we have a few ImageViews and a button in our board and all of these. If you look over here, I'm going to get rid of this, look at our design. 

So, you see on our component tree on the left over here, all of these mageViews buttons, everything, they are children of the parent and the parent is the ConstraintLayout, okay? And you can see this structure in our code as well. You see we have our ConstraintLayout that starts over here. Within it, we have all of these ImageViews and button and all of that. These are all the ConstraintLayouts, children that's something important to keep in mind. And the ConstraintLayout is also the root for binding on top over here where we are setting set content view in our onCreate method. So, what we can do is iterate through all of the children of the ConstraintLayout. So, iterate through all of the children, which means all of these views and replace the images from these ImageViews, okay? So, that's the concept. However, a couple of things we have to keep in mind is that the button that we have here, this is not an ImageView, okay? It's of type button.

So, we have to disregard that while we work through this, that's one part and the other is one of these image views, this one right here is our board and we don't want the board to disappear when we are resetting it. We want the board to stay there. So, with those couple of things in mind, let's go ahead and implement this. Back to my code down here, I'm going to first refer to my ConstraintLayout so binding.root. And then I can refer to all of the children by simply saying .children, you see? So, that's a collection right there of all the children that I have. And then I can iterate through each of them by saying .forEach, okay? So, open and close curly brace. Now, within this, I can refer to each of the children with it, which we have explored before. 

So, first let's make sure that it is not a button and the way to do that, is if it and the way to check for type is with is, okay? So, if it is Button, button with a capital B right there. If it is a Button class, this would make sure that we enter the if block, if it is a button, so to make sure it's not a button, we have to say not is button and then open and close curly brace. So, that will take care of disregarding the button when we do this and then we'll say val, we'll just call it a slot since we're going to go through the slots equals remember it which is all the children of our ConstraintLayout, they are views, so we have to cast each of them as ImageViews. So, it as ImageView right there. So, now that we have the slot that we are going to work with, all of our slots right here, they have tags, right? We have tag 0,1, 2, 3, 4, all of them have tags that are set except the board, the board does not have a tag. 

So, that could be one way of differentiating it. So, if the board doesn't have a tag, that means the board's tag is null. So, if we can say slot.tag != null, so that means the board will be skipped. Then we can set the image to be null or the image to be nothing. We'll do slot. to set it to null, all we have to do is say setImageDrawable and set it to no, okay? So, that will get rid of the image. All right, so let's test it out, run app. All right, we're ready to go. So... crosses have won, play again button pops up, let's tap it. Perfect. Empty board working perfectly. So, that was definitely a challenging exercise, and now you know how to update things on your layout while changing internal collections like the game state array and manipulating things like images and other layout settings and all of it programmatically. 

So, I hope you enjoyed building this project and have some fun playing a few games of tic tac toe on your own app. And don't worry if all of this is not clear to you yet and if some of the steps required don't come to you automatically, it wasn't supposed to, okay? This was pretty complex, and we will revisit these concepts of objects, types, typecasting, children, parents and all of that several times throughout the rest of the course. And with each visit, it'll get clearer. All right, great. Next, we're going to look at a couple more animation topics before wrapping up the section. See you in the next video.

 

About the Author

Mashrur is a full-time programming instructor specializing in programming fundamentals, web application development, machine learning and cyber security. He has been a technology professional for over a decade and has degrees in Computer Science and Economics. His niche is building comprehensive career focused technology courses for students entering new, complex, and challenging fields in today's technology space.

Covered Topics