1
00:00:05,510 --> 00:00:10,200
Alright so we've now got a list of photo objects, and a user interface to display them on,

2
00:00:10,460 --> 00:00:13,100
so the next step is to tie the two together.

3
00:00:13,190 --> 00:00:18,560
Now we're going to do that by setting the data to a recycler adapter that the recycler view can use,

4
00:00:18,920 --> 00:00:26,190
to display the list of photos. Now the principle's very similar to what we did when we used the List

5
00:00:26,190 --> 00:00:32,470
View to display the top 10 apps that we parsed out of the XML from Apple's RSS feed.

6
00:00:32,650 --> 00:00:35,250
Now the adapter takes data from a data source -

7
00:00:35,260 --> 00:00:42,700
in our case it's a list of photo objects - and packages the data into a ViewHolder object. The ViewHolders

8
00:00:42,700 --> 00:00:47,650
are sent to the RecyclerView whenever it requests more data.

9
00:00:47,650 --> 00:00:54,160
Now the RecyclerView will send back views as they scroll off the screen for the RecyclerAdaptedr to re-use,

10
00:00:54,560 --> 00:01:01,010
and that saves creating hundreds of views, and because they're encapsulated in a ViewHolder, this

11
00:01:01,020 --> 00:01:07,990
also saves having to call findViewById all the time, to get the widgets in the view. Now just like

12
00:01:08,020 --> 00:01:13,990
with the ListView, we'll see that when we run the program that only a few ViewHolders are created.

13
00:01:13,990 --> 00:01:19,390
So the diagram represents a few ViewHolders being created by the RecyclerAdapter,

14
00:01:19,450 --> 00:01:22,700
then a sort of circular exchange at the bottom of the diagram.

15
00:01:22,900 --> 00:01:27,520
as old views are sent to the adapter for recycling.

16
00:01:28,030 --> 00:01:33,310
So our RecyclerAdapter's going to do a bit more than that, because the Photo objects only contain the

17
00:01:33,320 --> 00:01:37,860
URL for the thumbnails, and not the images themselves.

18
00:01:38,530 --> 00:01:41,390
So it's going to be the job for the adapter to fetch the thumbnails,

19
00:01:41,450 --> 00:01:43,540
as the list is scrolled up and down.

20
00:01:43,540 --> 00:01:49,070
That's what the Flickr cloud at the top left is supposed to represent, by the way, the thumbnails being downloaded

21
00:01:49,090 --> 00:01:51,910
from Flickr as they're needed.

22
00:01:51,940 --> 00:01:56,770
Alight with that said, now let's have a look at the class and flow diagram for the app again.

23
00:01:57,070 --> 00:01:58,840
Now we've completed the right hand side

24
00:01:58,840 --> 00:02:01,620
a few videos ago, and that was working fine.

25
00:02:01,690 --> 00:02:07,590
We now need to concentrate on the FlickrRecyclerViewAdapter class and the FlickrImageViewHolder

26
00:02:07,600 --> 00:02:08,460
class,

27
00:02:08,490 --> 00:02:13,000
so we're going to make a start on that in this video. And once we've done that, we can then create the

28
00:02:13,000 --> 00:02:20,170
code for PhotoDetailsActivity to display the full photos, and for SearchActivity to change the search

29
00:02:20,170 --> 00:02:23,310
tags and get a different set of photos.

30
00:02:23,320 --> 00:02:29,750
But first things first, let's get those two classes created. Alright so back in Android studio,

31
00:02:29,770 --> 00:02:31,030
we're going to create a new class by

32
00:02:31,060 --> 00:02:33,070
right clicking on the package as we normally do,

33
00:02:34,370 --> 00:02:36,950
New, Kotlin File/Class.

34
00:02:37,070 --> 00:02:40,960
We're going to call this one FlickrRecyclerViewAdapter.

35
00:02:44,860 --> 00:02:47,760
OK we're going to select class from here and click on OK.

36
00:02:49,550 --> 00:02:53,610
And I've just made a typo there, I should really call that adapter with an er, is the correct spelling at

37
00:02:53,610 --> 00:02:54,450
least in Australia,

38
00:02:54,460 --> 00:03:00,520
So I'm going to rename that to adapter, press enter there.

39
00:03:01,280 --> 00:03:01,540
Alright,

40
00:03:01,550 --> 00:03:06,290
so we need to extend or subclass the recycler view dot adapter class,

41
00:03:06,410 --> 00:03:11,510
and we're going to use a generic type parameter to ensure that only our flicker image view holder objects

42
00:03:11,930 --> 00:03:13,750
can be used with this adapter.

43
00:03:14,090 --> 00:03:20,120
So I'm going to, at the end of the definition, class FlickrRecyclerViewAdapter. We're going to extend the

44
00:03:20,320 --> 00:03:27,150
RecyclerView dot Adapter class, do it's RecyclerView.Adapter,

45
00:03:30,410 --> 00:03:40,760
and we're going to add a diamond, then it's going to be FlickrImage, FlickrImageViewHolder, and we're

46
00:03:40,760 --> 00:03:43,070
going to get an error there because we haven't created that yet,

47
00:03:43,190 --> 00:03:45,340
and our parentheses on the end, outside of the,

48
00:03:45,350 --> 00:03:45,900
or

49
00:03:46,070 --> 00:03:54,110
after the greater than sign. And we're going to start then after that, on the next line, we're going to add

50
00:03:54,110 --> 00:04:01,360
a TAG as per normal, private val TAG equals, it's going to be Flickr

51
00:04:03,080 --> 00:04:04,210
RecyclerViewAdapter.

52
00:04:07,410 --> 00:04:08,790
OK.

53
00:04:09,460 --> 00:04:12,510
Now at this point we do have to be very careful here with this tag.

54
00:04:12,530 --> 00:04:14,860
In fact we have to be very, very careful.

55
00:04:14,860 --> 00:04:22,660
Now a log tag can be a maximum of 23 characters. Any longer than 23 characters and you'll get an error when

56
00:04:22,660 --> 00:04:25,140
you call one of the logging methods such as Log.d.

57
00:04:25,480 --> 00:04:31,420
Now logging's very useful to us, but it's not an essential part of the app. So it'd be a real shame

58
00:04:31,420 --> 00:04:34,900
to have our app crash just because we wanted to do some logging.

59
00:04:35,200 --> 00:04:39,300
But that's what will happen if I leave the tag set to what it is here,

60
00:04:39,490 --> 00:04:41,320
FlickrRecyclerViewAdapter.

61
00:04:41,560 --> 00:04:45,960
So it's very easy to just copy and paste the class name, and I've done that in the past,

62
00:04:45,970 --> 00:04:50,650
but be aware that there's a limit on the length of the tag, and make sure that you don't use more than

63
00:04:50,650 --> 00:04:54,940
23 characters as the name when you're using a tag.

64
00:04:54,940 --> 00:05:00,310
Now when using Android Studio for Java code, there's a shortcut that automatically truncates the tag

65
00:05:00,320 --> 00:05:02,910
name, but we don't have that for Kotlin code,

66
00:05:02,920 --> 00:05:03,660
not yet anyway,

67
00:05:03,670 --> 00:05:06,140
so watch out for long tag names.

68
00:05:06,400 --> 00:05:11,890
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

69
00:05:11,890 --> 00:05:16,200
it at FlickrRecyclerViewAdapt, basically deleting the er on the end.

70
00:05:16,220 --> 00:05:21,440
Now of course I could have avoided the problem by not using such a long name for the class, FlickrAdapter

71
00:05:21,440 --> 00:05:23,150
perhaps would have been just fine.

72
00:05:23,350 --> 00:05:28,240
But then I wouldn't have had an excuse to warn you about its potential for errors. Alright so moving on.

73
00:05:28,270 --> 00:05:33,800
Now main activity's going to provide our adapter with the list of photos.

74
00:05:33,850 --> 00:05:37,670
So therefore we need to store them in a photo list property.

75
00:05:37,700 --> 00:05:47,900
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.

76
00:05:48,060 --> 00:05:52,400
Then it's going to be list, then diamond operator, of photo objects.

77
00:05:52,490 --> 00:05:55,600
Alright so we've still get some errors here, we've got the first one, there's some

78
00:05:55,630 --> 00:05:58,170
methods that we need to override,

79
00:05:58,180 --> 00:06:03,340
we need to implement. That's relating to the on bind view holder and we'll come to that shortly.

80
00:06:03,420 --> 00:06:08,510
And also this other error because we haven't yet created the FlickrImageViewHolder class.

81
00:06:08,530 --> 00:06:10,930
So I'm going to go ahead and do that first.

82
00:06:10,960 --> 00:06:17,050
Now a common mistake here would be to declare the view holder as an inner class of the recycler adapter, but

83
00:06:17,050 --> 00:06:19,630
that can actually lead to memory leaks.

84
00:06:19,630 --> 00:06:22,480
Now in Java, we'd make the class static.

85
00:06:22,540 --> 00:06:28,240
Now in Kotlin we can place it in the same file as our adapter. The adapter will then have access to its

86
00:06:28,240 --> 00:06:32,660
properties, just like we did for the list view adapter in the top 10 downloader app.

87
00:06:32,950 --> 00:06:38,530
Now I'm mentioning Java here because you'll come across lots of example code in Java, when searching for

88
00:06:38,600 --> 00:06:40,540
Recycler view dot adapter.

89
00:06:40,720 --> 00:06:44,880
Now some of those examples use an inner class and fail to make it static.

90
00:06:45,070 --> 00:06:48,490
So watch out for that, and move the class to the start of the file,

91
00:06:48,670 --> 00:06:51,870
so that it's not actually inside the adapter class

92
00:06:51,880 --> 00:06:57,880
if you convert code like that to Kotlin. So to do that I'm going to come back here and start it at

93
00:06:57,880 --> 00:07:11,540
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,

94
00:07:11,760 --> 00:07:14,110
View with a capital V, then colon.

95
00:07:14,440 --> 00:07:23,620
And it's going to be RecyclerView.ViewHolder, parentheses view, and left and right curly braces, and then

96
00:07:23,630 --> 00:07:30,650
inside that we're going to have a couple of variables, var thumbnail colon, that's going to be an Image

97
00:07:30,650 --> 00:07:36,740
View equals view dot find view by id, R

98
00:07:40,390 --> 00:07:52,500
dot id dot thumbnail. And the next one's going to be var title colon TextView equals view dot

99
00:07:52,540 --> 00:07:58,550
find view by Id, parentheses R dot id dot title.

100
00:08:01,500 --> 00:08:05,000
And you can find that sometimes Android studio does some funny things there.

101
00:08:05,000 --> 00:08:10,900
You can notice that the view wasn't being resolved properly, but then it did actually eventually find and realize

102
00:08:10,900 --> 00:08:16,520
that it needed to import android dot view dot view, and that error disappeared. Alright I'll just leave that all

103
00:08:16,580 --> 00:08:22,110
showing now. Now this class I've just created, the FlickrImageViewHolder,

104
00:08:22,310 --> 00:08:27,880
well it's very similar to the view holder that we used for the list view in the top 10 downloader app.

105
00:08:28,100 --> 00:08:33,429
It just contains properties to store the image view and the text view that'll hold the data that we're displaying,

106
00:08:33,570 --> 00:08:35,980
and that's literally all the class does.

107
00:08:36,140 --> 00:08:41,030
It's basically just a way of storing references to the widgets in the view that'll be displayed by

108
00:08:41,030 --> 00:08:45,430
the recycler view, just like we did when we improved the adapter for the list view.

109
00:08:45,760 --> 00:08:48,770
Now the difference here though, is that we don't get a choice.

110
00:08:48,770 --> 00:08:50,850
We have to implement a view holder

111
00:08:50,900 --> 00:08:55,990
when using a recycler view. Now we've got an error because we still haven't implemented as required, on

112
00:08:55,990 --> 00:08:58,920
bind view holder method, it's this error here.

113
00:08:58,940 --> 00:09:04,090
There's actually two there, there's also going to be get item count that we need to implement as well.

114
00:09:04,350 --> 00:09:05,860
There's actually three in total,

115
00:09:05,930 --> 00:09:07,630
and we're going to implement those next,

116
00:09:07,630 --> 00:09:11,940
and that's going to be inside the FlickrRecyclerViewAdapter class.

117
00:09:11,980 --> 00:09:16,490
So let's go ahead and just make sure your cursor's within that class, and I'm going to use control

118
00:09:16,490 --> 00:09:20,320
I to implement that,

119
00:09:20,360 --> 00:09:26,090
and that's the same for both Mac and PC. I'm going to do get item, or implement to get item count on

120
00:09:26,100 --> 00:09:27,370
bind view holder.

121
00:09:27,510 --> 00:09:32,570
Also this onCreateViewHolder.

122
00:09:32,680 --> 00:09:38,360
OK, so that's now, we've now implemented, created the stubs at least for those functions,

123
00:09:38,360 --> 00:09:41,640
and we're ready to start implementing it. Now starting with on bind 

124
00:09:41,670 --> 00:09:48,350
view holder, this second one, note that the holder in on bind view holder, and actually also parent in on Create

125
00:09:48,360 --> 00:09:52,190
view holder, they're both declared as nullable types.

126
00:09:52,250 --> 00:09:57,500
Now that may change as Google get around to annotating the Android source code, but at the moment what

127
00:09:57,500 --> 00:10:00,800
I'm going to do is just delete the question mark from both.

128
00:10:00,890 --> 00:10:03,120
Firstly the one there in on bind view holder,

129
00:10:03,190 --> 00:10:09,340
then also coming down here to parent, in onCreate view holder, delete that one as well.

130
00:10:09,350 --> 00:10:10,710
Now is that safe to do?

131
00:10:10,910 --> 00:10:16,220
Well in this case, yes it is. Our app will soon crash if it isn't, but you can actually check out the

132
00:10:16,220 --> 00:10:17,980
recycler view source code

133
00:10:18,170 --> 00:10:24,620
if you're not sure. Unlike the list view, the recycler view will always provide a view holder when it calls on

134
00:10:24,620 --> 00:10:28,920
bind view holder, and for that reason it is safe to do what we've just done.

135
00:10:29,060 --> 00:10:36,080
OK, so on the onCreate view holder function, we'll just inflate a view from the browse dot XML layout that

136
00:10:36,080 --> 00:10:39,700
we've created and then return that view.

137
00:10:39,770 --> 00:10:46,580
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".

138
00:10:53,120 --> 00:10:54,130
Alright so we'll do a log

139
00:10:54,140 --> 00:11:00,560
first, so Log.d  parentheses TAg comma, double quotes, dot onCreate view holder,

140
00:11:03,990 --> 00:11:21,410
new view requested. Then we'll do val view is equal to LayoutInflater dot from parentheses parent dot context parentheses

141
00:11:21,420 --> 00:11:30,850
dot inflate, then within parentheses again it's going to be R dot layout dot browse comma parent comma

142
00:11:30,850 --> 00:11:32,760
false.

143
00:11:32,810 --> 00:11:36,720
And then lastly we've going to return Flickr view,

144
00:11:36,940 --> 00:11:40,790
sorry Flickr image view holder, parentheses view.

145
00:11:41,840 --> 00:11:43,000
So again this function

146
00:11:43,030 --> 00:11:44,420
is there to

147
00:11:44,600 --> 00:11:49,800
inflate a view from the browse dot XML layout we created, and then return that view.

148
00:11:49,810 --> 00:11:55,690
So in order to inflate the XML into a view we need to get an inflater. So the static layout inflater 

149
00:11:55,690 --> 00:12:00,390
dot from function returns an inflater for the provided context.

150
00:12:00,390 --> 00:12:07,310
In this case, we're using the context of the parent view group that was passed to us in the parent parameter.

151
00:12:07,330 --> 00:12:12,880
We're talking about the code on line 35. We're then calling the inflate function and giving it the resource

152
00:12:12,940 --> 00:12:18,700
ID of the XML file, and of course we saw browse dot XML a few videos ago when we set that up and created

153
00:12:18,700 --> 00:12:19,720
it.

154
00:12:19,840 --> 00:12:25,480
Now by the way a common error you'll see is null being passed as the parent parameter to the inflate

155
00:12:25,480 --> 00:12:29,440
method, and I actually mentioned this in the YouTube App videos.

156
00:12:29,440 --> 00:12:32,870
Now you might come across a line similar to this,

157
00:12:33,110 --> 00:12:36,850
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

158
00:12:36,850 --> 00:12:45,400
along the lines of this, which will sort of work, but unfortunately also sort of won't. Now without knowing

159
00:12:45,400 --> 00:12:46,770
the parent view,

160
00:12:47,130 --> 00:12:52,210
the inflater has no way of knowing things like what themes should be applied.

161
00:12:52,540 --> 00:12:56,860
So the styling of the widgets will just get the defaults, and they won't look right

162
00:12:56,860 --> 00:13:02,230
if you've made any changes to the themes. Now there's no excuse for doing that because we've been passed

163
00:13:02,230 --> 00:13:04,810
the parent view as a parameter.

164
00:13:05,050 --> 00:13:09,760
So I'm going to delete that line and we'll go back to that first version that actually does use the

165
00:13:09,760 --> 00:13:11,280
parent parameter.

166
00:13:11,560 --> 00:13:14,490
So I'm going to delete this one.

167
00:13:14,720 --> 00:13:16,580
Alright so the final parameter

168
00:13:16,580 --> 00:13:18,320
in the inflate method,

169
00:13:18,340 --> 00:13:23,730
this one here attached to route, that tells the inflater whether to attach, unsurprisingly, the view to it's root

170
00:13:23,740 --> 00:13:24,700
or not.

171
00:13:24,700 --> 00:13:26,310
Now we don't want to do that.

172
00:13:26,330 --> 00:13:29,360
The recycler view's going to take care of all that.

173
00:13:29,380 --> 00:13:33,960
In fact that's the reason why the incorrect function's often used. By passing

174
00:13:33,950 --> 00:13:36,250
null there's no route to attach to,

175
00:13:36,310 --> 00:13:39,550
so it works, but it's not the correct way to do things

176
00:13:39,550 --> 00:13:46,480
as I've mentioned. The correct way is to pass the parent, then pass false as the third parameter. So that

177
00:13:46,480 --> 00:13:50,690
tells the inflater not to attach the inflated view to its parent.

178
00:13:50,690 --> 00:13:54,730
Now attaching a view by the way, just means adding it to the parent layout,

179
00:13:54,910 --> 00:14:00,340
pretty much what we do when we drag a widget onto the layout designer, or when we use layout dot add view in

180
00:14:00,340 --> 00:14:06,160
the YouTube, or when we used rather, layout dot add view in the YouTube app, to add the player to the layout.

181
00:14:06,710 --> 00:14:11,830
Alright so I'm going to leave the on bind view holder function until last, because that's really the only interesting

182
00:14:11,830 --> 00:14:16,810
one. Let's simply get the other stuff out of the way first, so we're going to implement the get item count

183
00:14:16,810 --> 00:14:21,910
first, or next rather. We'll start off with a log, so Log.d

184
00:14:21,920 --> 00:14:34,720
parentheses, then it's going to be TAG comma double quotes dot get item count called, and we'll do return if parentheses

185
00:14:35,130 --> 00:14:38,320
photoList dot is not empty.

186
00:14:38,590 --> 00:14:46,090
Then closing parentheses there, photoList dot size else zero.

187
00:14:46,750 --> 00:14:50,520
So that just literally returns the number of photos in the list.

188
00:14:50,530 --> 00:14:52,970
Now we're using an if as an expression here,

189
00:14:53,200 --> 00:14:57,330
so it checks the condition and evaluates the photoList.size

190
00:14:57,370 --> 00:14:58,690
if the condition is true,

191
00:14:58,900 --> 00:15:00,740
otherwise evaluates to zero.

192
00:15:01,220 --> 00:15:04,170
Alright so there's two other functions that we're going to need,

193
00:15:04,410 --> 00:15:09,270
and as they're both really short I'm going to add them now. I'm going to put them immediately after get

194
00:15:09,290 --> 00:15:11,150
item counts,

195
00:15:11,980 --> 00:15:15,130
but firstly we want a load new data function, so fun

196
00:15:15,160 --> 00:15:16,520
LoadNewData,

197
00:15:16,890 --> 00:15:27,670
parentheses newPhotos colon list, diamond operator Photo. Then it's going to be photoList equals

198
00:15:27,670 --> 00:15:28,450
newPhotos,

199
00:15:31,250 --> 00:15:38,320
then we're doing notifyDataSetChanged parentheses, so we're calling that function.

200
00:15:38,350 --> 00:15:44,250
So basically this function takes the new list as a parameter and stores it in the photo list field.

201
00:15:44,260 --> 00:15:50,230
Now the notified data set change function tells the recycler view that the data has changed, so that it

202
00:15:50,230 --> 00:15:56,480
can refresh the display. To be more accurate it tells any registered observers that the data has changed,

203
00:15:56,560 --> 00:16:02,860
and you'll see that function called in other places, when a recycler view is not being used, but here the registered

204
00:16:02,860 --> 00:16:05,470
observer is our recycler view.

205
00:16:05,630 --> 00:16:08,950
Alright so the other thing we're going to want to do here is to get the full picture,

206
00:16:09,070 --> 00:16:11,820
when the user taps one of the thumbnails in the list.

207
00:16:11,920 --> 00:16:17,100
So main activity will need to get the photo record for the item that was tapped.

208
00:16:17,200 --> 00:16:19,350
So the function we need here is fun,

209
00:16:19,660 --> 00:16:24,290
get photo parentheses position

210
00:16:24,670 --> 00:16:30,420
colon int, then we're going to return colon photo questionmark,

211
00:16:30,830 --> 00:16:39,100
then add our left and right curly braces, and the code's going to be return if parentheses photoList dot

212
00:16:39,520 --> 00:16:41,000
is not empty,

213
00:16:41,110 --> 00:16:46,610
closing parentheses, photoList square brackets position,

214
00:16:46,620 --> 00:16:56,260
else null. Now an activity that can call the get photo function to retrieve the details for a photo from its position

215
00:16:56,260 --> 00:17:01,720
in the list when it needs one, and that'll be useful when a thumbnail is tapped to provide the full

216
00:17:01,720 --> 00:17:04,150
details of the photo to display.

217
00:17:04,150 --> 00:17:05,640
Now we should try not to return

218
00:17:05,640 --> 00:17:07,530
null in Kotlin. Sometimes though,

219
00:17:07,569 --> 00:17:09,280
it is still necessary.

220
00:17:09,310 --> 00:17:12,839
Now we're going to display a placeholder image if there is no matching photos,

221
00:17:13,000 --> 00:17:18,910
so in that case, photo list's going to be empty, and there's not really anything else sensible that we can

222
00:17:18,910 --> 00:17:21,800
return. Alright so let's end the video here now.

223
00:17:21,880 --> 00:17:26,859
In the next one we're going to write the code for the remaining function, on bind view holder. See

224
00:17:26,859 --> 00:17:27,810
you in the next video.

