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.