In this video we going to add the code to display the full photo when one of the items in the recycler view
list is clicked, or tapped, to be more accurate.
So let's have a look at our diagram to see what we're doing. Now in the last few videos we finished
the recycler item click listener, to let our app respond to clicks and long presses on the items
in the recycler adapter.
So we've implemented a lot of the functionality of this app.
We just need to get the three activities working together to display the full photos, and allow the tags
to be changed to search for different photos.
So we're going to implement the search facility later,
but in this video we're going to focus on displaying the full photo.
Now in the process, we're going to review how to launch one activity from another using intents,
and that's a fundamental aspect of Android programming,
and one we first saw in the YouTube app.
So let's get main activity to launch the photo details activity, when a photo is tapped in the list.
So we're going to start by creating that base class, base activity, that our activities will extend.
The reason for doing that is to share function and properties amongst the number of different activities,
three in our case. By defining common functions in a single class, and then having the other classes extend
it, they'll get access to everything that's defined in the base class,
and that means we don't have to include the same function in each activity. They're only going to be sharing a single function,
but it's still a very valuable technique.
Alright so coming back to Android Studio, let's start by right clicking the package name - New Kotlin
File/Class, Kind Class, and the name for the new class, we're going to go with BaseActivity.
Now although this is going to be an activity,
we're not going to be using the wizard, and that's because we don't want a layout created for it and we
don't want it adding to the manifest.
So the name as you can see is BaseActivity, and it'll be a class which I've selected, and I'm going to click OK.
So this is for our activity base class.
So for that reason I'm going to extend app compat activity, colon AppCompatActivity, and we also want
a TAG as per normal, so private val TAG equals double quotes BaseActivity.
Now I'm also going to define a couple of string constants that we'll use when transferring data between
activities. Now when we launched new activities in the YouTube app,
we didn't need to send any data between the activities.
But in this app, when we want to display a photo in the photo details activity, we'll need to send
the details of the photo to the activity that's launched, and you'll see how to do that and why we need
these strings in a minute. So I'm going to start by typing internal const
val flickr query is equal to flickr query in double quotes. Then the next one, internal const
val photo underscore transfer
equals double quotes photo underscore transfer.
And actually want I should have done is define that outside of the class.
So I'll go ahead and do that.
I'll cut that out because it should have actually been outside, like so.
Now I've made these constants internal because they don't have to be available outside this project.
It's unlikely that we'd include this package in another, but it's a good habit to develop.
Now we're only going to add one function to this base class.
Now the function will be used to show the toolbar, and it'll also allow an activity to choose whether the
toolbar should have the home button or not.
Now main activity won't need a home button because it is the home screen, but the other activities will.
So when we launch photo details activity from main activity for example, the home button will allow
us to get back, or go back, to main activity.
Now as you're typing this in, if you're prompted for the toolbar package to import, make sure you choose
the support library, the Android dot support dot v 7 dot widget dot toolbar, and you should see that happens automatically
as I'm typing. So I'm going to create that function.
It's going to be internal, fun activateToolbar parentheses, and it's going to enableHome colon Boolean, and then we
get a code block. And we'll start with a log,
so Log.d parentheses TAG comma double quote dot activateToolbar. Then we're going to do val
toolbar is equal to findViewById, and within the diamond operator we're going to do View,
and within the parentheses it's going to be R dot id dot toolbar, closing parentheses as Toolbar with a capital T. And
again this is where I said it's important to choose the correct import.
So we want to choose the android dot support dot
v 7 dot widget.
OK, next we're going to type setSupportAction toolbar, toolbar,
and then finally supportAction
Bar questionmark dot set Display Home as up enabled, enableHome.
So that's actually the code.
That's our base activity created, and actually I should have fixed that up as well, added a parentheses, or add a parentheses
at the end of that
to make that line valid, because we're obviously extending AppCompatActivity. So again that's all there
is to base activity. It's created. The other activities in the app will extend this class instead of
AppCompatActivity, so that they can use this activate toolbar function. And all the function does is inflate
the toolbar from the toolbar dot XML file, then use the setSupportActionBar function with the inflated
toolbar, to put the toolbar in place at the top of the screen.
Now the action bar will automatically add the home button if we tell it to.
And you probably guessed that we do that by getting a reference to the new action bar, and calling setDisplayHome
AsUpEnabled to enable or disable the home button, depending on the parameter passed into the function.
Now there's one other important change we need to make. Kotlin defaults to not allowing classes to be
subclassed.
Now we do want to extend base activity.
That's the reason we're creating it after all.
So to allow it to be extended, we have to go back and mark it as open.
So we're going to do that on line 15, open to make it
now able to be subclassed. Now if I just hover over that we're getting a warning over here, about
the class not being registered in the manifest.
That's actually why the class name itself's highlighted.
Now the light bulb lets us suppress that warning,
and that would actually be worth doing here.
Our app won't create instances of base activity,
so there's actually no point in registering it.
So for that reason I suggest you actually use the toolbar,
and select this option, suppress registered, so I'm going to click on that, and you can see that that warning disappears.
And we've got this annotation at suppressed lint for registered. Alright, so now that we've done that, let's actually
change the three activity classes and also do a bit of tidying up.
So I'm going to start from the bottom.
So let's open search activity first.
I want to change this
so I'm going to open up the import just so you can see what's happening.
We should find, as I start removing some of these things, that the imports get updated.
So firstly for AppCompatActivity, we want to remove that and make that BaseActivity.
Now I've seen that the import's updated automatically, and we also want to make sure now that we've got a
tag, so let's add a tag as well.
So we'll do a private val TAG equals SearchActivity.
And if Android Studio didn't delete the imports for you,
then you want to go ahead and do those manually, but again because I've configured Android Studio to
automatically remove unused imports, it did that for me automatically.
Now in the onCreate method, we want to use the ActivateToolbar function to set the toolbar.
So we want to comment out this setSupportAction toolbar line, and what we can actually do is just actually remove
both of those lines.
And instead what we want to do is call our function activateToolbar,
and we want to pass true to this case,
in this case, because we do want the home button to appear on the search activity, and we want to do a Log.d
parentheses TAG comma double quotes, and it's going to be dot onCreate ends, and I didn't put one at the
start so let's do that as well.
First line there, starts.
Now make sure that you do keep an eye on the imports and delete any unused ones that may get left
behind,
again, if you haven't configured your version of Android Studio to automatically reorganise imports.
Alright so that's search activity. We need to do the same thing for photo details activity, so
let's bring that on the screen.
So firstly I'm going to change AppCompatActivity again to be BaseActivity, and I'm going to take the opportunity
to clean the fab code up, because we're not going to be calling that anymore. I'm going to delete
that, and also delete this set
Action, setSupportAction toolbar.
And instead what we're going to do is call the activateToolbar, passing true because again for photo details
activity, we do want the home button to be an option so that we can go back to the main activity. So again
tidy up any imports if necessary.
So finally now, main activity, let's open that up.
So we're going to start by replacing AppCompatActivity again,
BaseActivity. We want our super and our set content view but we don't want the setSupportActionBar line there, I'm
going to delete that. Instead what I'm going to do on that line and is do an activateToolbar.
This time we want to pass false,
and that's because we don't want the home button to appear, because we're already on the home page which
is main activity. Now check the imports here as well.
Now the synthetic import of activity underscore main isn't needed,
now that we're not accessing the toolbar directly.
At the moment here you can see we've just got content underscore main still showing. Android Studio won't always delete
the import though, so keep that in mind.
OK so that's the the housekeeping out of the way.
It's time now to make our onItemClick methods, functions do something useful. At the moment
if you recall, they're only just displaying Toast messages.
So what we want is for a tap, or a long press of an item in the recycler view to launch the photo
details activity, providing it with the details of the photograph to display.
So we have to decide which of the two events to use,
and for most applications, the onItemClick function's probably the one that users would expect.
In other words they just tap the photo and it opens full screen for viewing.
Now there are some apps where you might decide to use the long press instead.
For example, a sat nav application aimed at motorcyclists. If you consider the use case for an app like
that, the phone or tablet would probably be inside a waterproof case, fixed to the handlebars, and the rider
would often be wearing thick gloves.
So in a situation like that,
the long press is a better option to make the app more usable,
otherwise it's far too easy for the rider to trigger a
tap when they didn't intend to.
So the conditions that the app will be used in, will help to decide which touch event we should be using.
So I'm going to use the long press, just so that we could be sure it works
after our discussion in the previous video. And if you remember the long press function in the recyclerItem
ClickListener, didn't return a true or false value.
So this is actually a good opportunity to make sure it still works despite that.
So we're going to modify the onItemLongClick function.
So I'm just going to come down here, and you can see that we've got the code here. What I'm going to do
first is get rid of the Toast message now because we're going to be using that anymore.
And the code we want to introduce here is val photo equals flickrRecyclerViewAdaper
dot getPhoto parentheses position. I accidentally added two parentheses there, it should just be the one. Then on
the next line,
if parentheses photo is not equal to null, code block, then we're going to do val intent equals Intent parentheses this comma,
and it's going to be PhotoDetailsctivity colon colon class.java.
On the next line we're going to put intent dot putExtra, putExtra parentheses, and it's going to be photo
underscore transfer comma photo.
Then we're going to startActivity
intent.
Now we had to write the getPhoto function if you recall, that we're calling here on line 47, to return the nullable
photo type, which means we need this null check which we're doing on line 48.
And next we use an intent to launch the PhotoDetailsActivity.
Now this is exactly the same as we did in the YouTube app, when we launched the standalone activity from a button
on the main screen.
So we create a new intent, then we use this as a context, this is the code on line 49.
That's fine because this class extends BaseActivity, which itself extends AppCompatActivity,
so our main activity class is an activity, and can be used whenever a context is required.
Now the second parameter is the activity class that we want to launch.
And remember that we put colon colon class dot Java after the class name, to create a class literal
that we can use to pass the class as a parameter.
Now the next thing we need to do before starting the activity, is provide the photo details to the
intent.
Now we didn't have any information to pass to the new activity in the YouTube app, but here we have to tell the
PhotoDetailsActivity which photo it should display, and we can do that by using the putExtra function
to add data to the intent.
You can see we're doing that on line 50. Now putExtra can be used to add simple data such as integers and
strings, but you can also send more complex objects, even photo objects. And it actually works very similar
to the bundles that we used when saving the activity state,
for when it's restored after an orientation change. So you need to provide a key and a value,
then whatever needs to access the data, uses the key to retrieve it.
So that's why we defined the photo underscore transfer constant in the base activity class.
So both main activity and photo details activity need to use the same key,
so we use a constant to make sure we don't type the key differently in one class. But we have got this
error though as you can see again on line 50.
Now I said that putExtra can be used to add more complex objects to the intent,
but there is a condition that the object must meet for it all to work,
and that object has to be serializable.
So I'm going to stop the video here, and in the next video we're going to look at the serializable interface,
and how to implement it.
So I'll see you in the next video.