Alright so we've now got a list of photo objects, and a user interface to display them on,
so the next step is to tie the two together.
Now we're going to do that by setting the data to a recycler adapter that the recycler view can use,
to display the list of photos. Now the principle's very similar to what we did when we used the List
View to display the top 10 apps that we parsed out of the XML from Apple's RSS feed.
Now the adapter takes data from a data source -
in our case it's a list of photo objects - and packages the data into a ViewHolder object. The ViewHolders
are sent to the RecyclerView whenever it requests more data.
Now the RecyclerView will send back views as they scroll off the screen for the RecyclerAdaptedr to re-use,
and that saves creating hundreds of views, and because they're encapsulated in a ViewHolder, this
also saves having to call findViewById all the time, to get the widgets in the view. Now just like
with the ListView, we'll see that when we run the program that only a few ViewHolders are created.
So the diagram represents a few ViewHolders being created by the RecyclerAdapter,
then a sort of circular exchange at the bottom of the diagram.
as old views are sent to the adapter for recycling.
So our RecyclerAdapter's going to do a bit more than that, because the Photo objects only contain the
URL for the thumbnails, and not the images themselves.
So it's going to be the job for the adapter to fetch the thumbnails,
as the list is scrolled up and down.
That's what the Flickr cloud at the top left is supposed to represent, by the way, the thumbnails being downloaded
from Flickr as they're needed.
Alight with that said, now let's have a look at the class and flow diagram for the app again.
Now we've completed the right hand side
a few videos ago, and that was working fine.
We now need to concentrate on the FlickrRecyclerViewAdapter class and the FlickrImageViewHolder
class,
so we're going to make a start on that in this video. And once we've done that, we can then create the
code for PhotoDetailsActivity to display the full photos, and for SearchActivity to change the search
tags and get a different set of photos.
But first things first, let's get those two classes created. Alright so back in Android studio,
we're going to create a new class by
right clicking on the package as we normally do,
New, Kotlin File/Class.
We're going to call this one FlickrRecyclerViewAdapter.
OK we're going to select class from here and click on OK.
And I've just made a typo there, I should really call that adapter with an er, is the correct spelling at
least in Australia,
So I'm going to rename that to adapter, press enter there.
Alright,
so we need to extend or subclass the recycler view dot adapter class,
and we're going to use a generic type parameter to ensure that only our flicker image view holder objects
can be used with this adapter.
So I'm going to, at the end of the definition, class FlickrRecyclerViewAdapter. We're going to extend the
RecyclerView dot Adapter class, do it's RecyclerView.Adapter,
and we're going to add a diamond, then it's going to be FlickrImage, FlickrImageViewHolder, and we're
going to get an error there because we haven't created that yet,
and our parentheses on the end, outside of the,
or
after the greater than sign. And we're going to start then after that, on the next line, we're going to add
a TAG as per normal, private val TAG equals, it's going to be Flickr
RecyclerViewAdapter.
OK.
Now at this point we do have to be very careful here with this tag.
In fact we have to be very, very careful.
Now a log tag can be a maximum of 23 characters. Any longer than 23 characters and you'll get an error when
you call one of the logging methods such as Log.d.
Now logging's very useful to us, but it's not an essential part of the app. So it'd be a real shame
to have our app crash just because we wanted to do some logging.
But that's what will happen if I leave the tag set to what it is here,
FlickrRecyclerViewAdapter.
So it's very easy to just copy and paste the class name, and I've done that in the past,
but be aware that there's a limit on the length of the tag, and make sure that you don't use more than
23 characters as the name when you're using a tag.
Now when using Android Studio for Java code, there's a shortcut that automatically truncates the tag
name, but we don't have that for Kotlin code,
not yet anyway,
so watch out for long tag names.
So I'm going to delete the extra characters so our app doesn't crash, so I'm going to come over here and leave
it at FlickrRecyclerViewAdapt, basically deleting the er on the end.
Now of course I could have avoided the problem by not using such a long name for the class, FlickrAdapter
perhaps would have been just fine.
But then I wouldn't have had an excuse to warn you about its potential for errors. Alright so moving on.
Now main activity's going to provide our adapter with the list of photos.
So therefore we need to store them in a photo list property.
So let's go ahead and add that, so after the class name I'm going to put parentheses, private space var space photoList colon.
Then it's going to be list, then diamond operator, of photo objects.
Alright so we've still get some errors here, we've got the first one, there's some
methods that we need to override,
we need to implement. That's relating to the on bind view holder and we'll come to that shortly.
And also this other error because we haven't yet created the FlickrImageViewHolder class.
So I'm going to go ahead and do that first.
Now a common mistake here would be to declare the view holder as an inner class of the recycler adapter, but
that can actually lead to memory leaks.
Now in Java, we'd make the class static.
Now in Kotlin we can place it in the same file as our adapter. The adapter will then have access to its
properties, just like we did for the list view adapter in the top 10 downloader app.
Now I'm mentioning Java here because you'll come across lots of example code in Java, when searching for
Recycler view dot adapter.
Now some of those examples use an inner class and fail to make it static.
So watch out for that, and move the class to the start of the file,
so that it's not actually inside the adapter class
if you convert code like that to Kotlin. So to do that I'm going to come back here and start it at
the top of the class. I'm going to do class Flickr, and you can see it's already helping us, it's image View holder, parentheses view colon,
View with a capital V, then colon.
And it's going to be RecyclerView.ViewHolder, parentheses view, and left and right curly braces, and then
inside that we're going to have a couple of variables, var thumbnail colon, that's going to be an Image
View equals view dot find view by id, R
dot id dot thumbnail. And the next one's going to be var title colon TextView equals view dot
find view by Id, parentheses R dot id dot title.
And you can find that sometimes Android studio does some funny things there.
You can notice that the view wasn't being resolved properly, but then it did actually eventually find and realize
that it needed to import android dot view dot view, and that error disappeared. Alright I'll just leave that all
showing now. Now this class I've just created, the FlickrImageViewHolder,
well it's very similar to the view holder that we used for the list view in the top 10 downloader app.
It just contains properties to store the image view and the text view that'll hold the data that we're displaying,
and that's literally all the class does.
It's basically just a way of storing references to the widgets in the view that'll be displayed by
the recycler view, just like we did when we improved the adapter for the list view.
Now the difference here though, is that we don't get a choice.
We have to implement a view holder
when using a recycler view. Now we've got an error because we still haven't implemented as required, on
bind view holder method, it's this error here.
There's actually two there, there's also going to be get item count that we need to implement as well.
There's actually three in total,
and we're going to implement those next,
and that's going to be inside the FlickrRecyclerViewAdapter class.
So let's go ahead and just make sure your cursor's within that class, and I'm going to use control
I to implement that,
and that's the same for both Mac and PC. I'm going to do get item, or implement to get item count on
bind view holder.
Also this onCreateViewHolder.
OK, so that's now, we've now implemented, created the stubs at least for those functions,
and we're ready to start implementing it. Now starting with on bind
view holder, this second one, note that the holder in on bind view holder, and actually also parent in on Create
view holder, they're both declared as nullable types.
Now that may change as Google get around to annotating the Android source code, but at the moment what
I'm going to do is just delete the question mark from both.
Firstly the one there in on bind view holder,
then also coming down here to parent, in onCreate view holder, delete that one as well.
Now is that safe to do?
Well in this case, yes it is. Our app will soon crash if it isn't, but you can actually check out the
recycler view source code
if you're not sure. Unlike the list view, the recycler view will always provide a view holder when it calls on
bind view holder, and for that reason it is safe to do what we've just done.
OK, so on the onCreate view holder function, we'll just inflate a view from the browse dot XML layout that
we've created and then return that view.
So let's write the code for that, and we'll put a note here, that's "Called by the layout manager when it needs a new view".
Alright so we'll do a log
first, so Log.d parentheses TAg comma, double quotes, dot onCreate view holder,
new view requested. Then we'll do val view is equal to LayoutInflater dot from parentheses parent dot context parentheses
dot inflate, then within parentheses again it's going to be R dot layout dot browse comma parent comma
false.
And then lastly we've going to return Flickr view,
sorry Flickr image view holder, parentheses view.
So again this function
is there to
inflate a view from the browse dot XML layout we created, and then return that view.
So in order to inflate the XML into a view we need to get an inflater. So the static layout inflater
dot from function returns an inflater for the provided context.
In this case, we're using the context of the parent view group that was passed to us in the parent parameter.
We're talking about the code on line 35. We're then calling the inflate function and giving it the resource
ID of the XML file, and of course we saw browse dot XML a few videos ago when we set that up and created
it.
Now by the way a common error you'll see is null being passed as the parent parameter to the inflate
method, and I actually mentioned this in the YouTube App videos.
Now you might come across a line similar to this,
and I'm just going to paste some code on there just to give you an idea what I'm talking about. So you might see something
along the lines of this, which will sort of work, but unfortunately also sort of won't. Now without knowing
the parent view,
the inflater has no way of knowing things like what themes should be applied.
So the styling of the widgets will just get the defaults, and they won't look right
if you've made any changes to the themes. Now there's no excuse for doing that because we've been passed
the parent view as a parameter.
So I'm going to delete that line and we'll go back to that first version that actually does use the
parent parameter.
So I'm going to delete this one.
Alright so the final parameter
in the inflate method,
this one here attached to route, that tells the inflater whether to attach, unsurprisingly, the view to it's root
or not.
Now we don't want to do that.
The recycler view's going to take care of all that.
In fact that's the reason why the incorrect function's often used. By passing
null there's no route to attach to,
so it works, but it's not the correct way to do things
as I've mentioned. The correct way is to pass the parent, then pass false as the third parameter. So that
tells the inflater not to attach the inflated view to its parent.
Now attaching a view by the way, just means adding it to the parent layout,
pretty much what we do when we drag a widget onto the layout designer, or when we use layout dot add view in
the YouTube, or when we used rather, layout dot add view in the YouTube app, to add the player to the layout.
Alright so I'm going to leave the on bind view holder function until last, because that's really the only interesting
one. Let's simply get the other stuff out of the way first, so we're going to implement the get item count
first, or next rather. We'll start off with a log, so Log.d
parentheses, then it's going to be TAG comma double quotes dot get item count called, and we'll do return if parentheses
photoList dot is not empty.
Then closing parentheses there, photoList dot size else zero.
So that just literally returns the number of photos in the list.
Now we're using an if as an expression here,
so it checks the condition and evaluates the photoList.size
if the condition is true,
otherwise evaluates to zero.
Alright so there's two other functions that we're going to need,
and as they're both really short I'm going to add them now. I'm going to put them immediately after get
item counts,
but firstly we want a load new data function, so fun
LoadNewData,
parentheses newPhotos colon list, diamond operator Photo. Then it's going to be photoList equals
newPhotos,
then we're doing notifyDataSetChanged parentheses, so we're calling that function.
So basically this function takes the new list as a parameter and stores it in the photo list field.
Now the notified data set change function tells the recycler view that the data has changed, so that it
can refresh the display. To be more accurate it tells any registered observers that the data has changed,
and you'll see that function called in other places, when a recycler view is not being used, but here the registered
observer is our recycler view.
Alright so the other thing we're going to want to do here is to get the full picture,
when the user taps one of the thumbnails in the list.
So main activity will need to get the photo record for the item that was tapped.
So the function we need here is fun,
get photo parentheses position
colon int, then we're going to return colon photo questionmark,
then add our left and right curly braces, and the code's going to be return if parentheses photoList dot
is not empty,
closing parentheses, photoList square brackets position,
else null. Now an activity that can call the get photo function to retrieve the details for a photo from its position
in the list when it needs one, and that'll be useful when a thumbnail is tapped to provide the full
details of the photo to display.
Now we should try not to return
null in Kotlin. Sometimes though,
it is still necessary.
Now we're going to display a placeholder image if there is no matching photos,
so in that case, photo list's going to be empty, and there's not really anything else sensible that we can
return. Alright so let's end the video here now.
In the next one we're going to write the code for the remaining function, on bind view holder. See
you in the next video.