1 00:00:05,290 --> 00:00:11,570 Welcome back. Okay, we finished the last video by looking at the text that 2 00:00:11,570 --> 00:00:16,910 we've hard-coded into the app. We should put the text into string resources, just 3 00:00:16,910 --> 00:00:22,250 like we do for layouts. I'll start with the name TextView that displays the 4 00:00:22,250 --> 00:00:30,290 heading. Click on the word "instructions" and expand the drop-down that appears, on 5 00:00:30,290 --> 00:00:38,090 the left hand side. We want Extract String Resource from the menu, and we'll 6 00:00:38,090 --> 00:00:52,070 call this instructions_heading. Click OK and Android studio 7 00:00:52,070 --> 00:00:58,039 replaces the text with a string resource. Unfortunately it doesn't do a perfect 8 00:00:58,039 --> 00:01:04,519 job, and we've got an error. This is because we don't have a context readily 9 00:01:04,519 --> 00:01:12,530 available, to call the getstring function on. As it happens, we don't need one. The 10 00:01:12,530 --> 00:01:18,200 setText function will accept a string resource ID instead. We can delete the 11 00:01:18,200 --> 00:01:24,380 call to context.getString, and the extra parentheses, to get rid of the 12 00:01:24,380 --> 00:01:36,039 error. Do the same thing in description. This time call the resource instructions. 13 00:01:37,780 --> 00:01:43,820 And that didn't go well. You'll often find that long strings in code are split 14 00:01:43,820 --> 00:01:49,340 up, like we've done here. But if you're in the habit of doing that, then it's a good 15 00:01:49,340 --> 00:01:55,009 habit to break, when writing Android apps. You'll almost always want to extract the 16 00:01:55,009 --> 00:02:00,289 string into a string resource, and that's much easier if you haven't split the 17 00:02:00,289 --> 00:02:06,969 text up like this. Ok, I'll undo that change. 18 00:02:10,360 --> 00:02:15,760 Now I need to put all the text back into a single line, then extract the resource 19 00:02:15,760 --> 00:02:21,070 again. It's a bit fiddly, which is why I suggest you don't split the text up in 20 00:02:21,070 --> 00:02:28,560 the first place. The light bulb can help here again. It has an option Convert 21 00:02:28,560 --> 00:02:35,200 Concatenation to Template. That removes the concatenation, and we should now be 22 00:02:35,200 --> 00:02:41,650 able to use a light bulb again to create the string resource. I say "should" because 23 00:02:41,650 --> 00:02:47,020 I've been unable to get that option to appear. It's not because we used the 24 00:02:47,020 --> 00:02:52,060 Convert to Template option either. At the moment, Android Studio won't provide the 25 00:02:52,060 --> 00:02:57,910 string resource option for this text. Whether that's because it's too long, or 26 00:02:57,910 --> 00:03:02,920 because it contains line break characters, I don't know. That might 27 00:03:02,920 --> 00:03:08,890 change in a later version, but for the moment it doesn't work. As we've said 28 00:03:08,890 --> 00:03:15,400 before, "life's too short to fight the tools". We know what the option should do 29 00:03:15,400 --> 00:03:21,519 so let's just do it. I'll cut all the text for the description, without the 30 00:03:21,519 --> 00:03:24,150 quote marks, 31 00:03:30,500 --> 00:03:40,860 then open up the strings.xml file from resources values. I'll add the new 32 00:03:40,860 --> 00:03:57,090 resource after instructions heading calling it instructions, and paste the 33 00:03:57,090 --> 00:04:05,190 text. The right-click context menu has a paste simple option, which will paste the 34 00:04:05,190 --> 00:04:09,870 text in, keeping the \ns instead of replacing them with line 35 00:04:09,870 --> 00:04:15,330 breaks. While we're here, there's a blank fragment resource that was created by the 36 00:04:15,330 --> 00:04:21,660 wizard, when we created the add/edit fragment. We don't need that so it can be 37 00:04:21,660 --> 00:04:26,090 deleted, along with the TODO comment. 38 00:04:28,220 --> 00:04:41,250 Back in the adapter, we use the same name, instructions, as a resource ID. Okay the 39 00:04:41,250 --> 00:04:52,950 next function we have to implement is getItemCount. The RecyclerView uses 40 00:04:52,950 --> 00:04:58,139 this so it knows how many items there are to display. If there are no items, 41 00:04:58,139 --> 00:05:03,570 we'll be sending back a view that displays the instructions. So we don't 42 00:05:03,570 --> 00:05:08,160 want to tell the RecyclerView that there are no records, we'll always return at 43 00:05:08,160 --> 00:05:10,760 least one. 44 00:05:16,710 --> 00:05:20,520 we'll log the start 45 00:05:20,760 --> 00:05:27,560 We'll create a variable called cursor of type Cursor to hold the cursor. 46 00:05:27,560 --> 00:05:34,110 We'll make a count variable which is of type integer, and if it is either null or 47 00:05:34,110 --> 00:05:39,270 it doesn't have any items in it, in other words the count is zero, we're going to 48 00:05:39,270 --> 00:05:42,600 return a value of one. This is a bit of a fib 49 00:05:42,600 --> 00:05:46,500 because actually we'll return a single view holder that's containing the 50 00:05:46,500 --> 00:05:49,880 instructions that we want to show. 51 00:05:50,500 --> 00:06:00,349 Otherwise let's return the count, then let's log the fact that we've finished, 52 00:06:00,349 --> 00:06:08,090 and actually return the value. That's all the overridden functions done, but 53 00:06:08,090 --> 00:06:14,949 there's one more function we still need to add. Google haven't created a basic 54 00:06:14,949 --> 00:06:21,080 CursorRecyclerViewAdapter class, so we're modifying RecyclerView's Adapter 55 00:06:21,080 --> 00:06:26,990 class. That's what we're extending, to produce this class. But the adapter 56 00:06:26,990 --> 00:06:32,300 doesn't know about cursors, so it doesn't have functions to deal with them. The 57 00:06:32,300 --> 00:06:39,680 CursorAdapter class has one additional function, and that's swapCursor. We're 58 00:06:39,680 --> 00:06:43,699 implementing the functionality of a CursorAdapter that's tailored for a 59 00:06:43,699 --> 00:06:50,539 RecyclerView, so it makes sense to use the same name for our function. We'll add 60 00:06:50,539 --> 00:06:57,830 it after the getItemCount function, and then discuss what it's doing. We'll 61 00:06:57,830 --> 00:07:02,030 comment our function, to help others that look at our code, and to help our future 62 00:07:02,030 --> 00:07:04,659 selves. 63 00:07:20,710 --> 00:07:27,970 We'll define our function to take a new cursor and return a cursor. If the old 64 00:07:27,970 --> 00:07:32,949 cursor and the new cursor are the same, then we're not really doing a swap and so 65 00:07:32,949 --> 00:07:44,009 we'll return null. We'll save the current itemCount, and we'll save our old cursor. 66 00:07:45,000 --> 00:07:54,190 We'll create our new cursor, and if that new cursor isn't null then we'll notify 67 00:07:54,190 --> 00:07:57,930 the observers about the new cursor. 68 00:08:00,630 --> 00:08:07,720 Otherwise we'll remove all the items, and notify the observers that that has 69 00:08:07,720 --> 00:08:14,710 happened. Finally, we'll return the cursor as promised. This basically allows the 70 00:08:14,710 --> 00:08:19,389 adaptors cursor to be swapped, when the underlying data changes and has to be 71 00:08:19,389 --> 00:08:26,410 requeried. This is boilerplate code. You'll find examples like this a lot online. It 72 00:08:26,410 --> 00:08:31,349 should be called whenever the cursor that the adapters using is changed. 73 00:08:31,349 --> 00:08:36,909 We'll see it being used in MainActivityFragment, when we provide a valid cursor 74 00:08:36,909 --> 00:08:41,799 for the first time, and then whenever the data changes. This function returns the 75 00:08:41,799 --> 00:08:46,330 previous cursor, which can be useful if the owner of the cursor needs to close 76 00:08:46,330 --> 00:08:52,420 it. In code that uses a CursorLoader, we didn't have to worry about this because 77 00:08:52,420 --> 00:08:57,880 the CursorLoader class takes care of it. But that's how the CursorAdapter does 78 00:08:57,880 --> 00:09:02,230 things, and I've stuck with the standard code so that it can be used with a 79 00:09:02,230 --> 00:09:08,980 custom Loader if needed. As it turns out, we do need to close the old cursor; and 80 00:09:08,980 --> 00:09:13,990 now that the Loader classes have been deprecated, we lose the functionality 81 00:09:13,990 --> 00:09:19,830 that they provide. The Loader took care of things like closing old cursors, and 82 00:09:19,830 --> 00:09:24,990 also made sure that database queries were executed on a background thread. 83 00:09:24,990 --> 00:09:29,890 Don't feel too bad that you can't use them though, they were a bit cumbersome 84 00:09:29,890 --> 00:09:34,150 to set up, and things aren't any harder now that we can't use them. 85 00:09:34,150 --> 00:09:39,430 in fact I think things are a bit easier now, as you'll see when we come to create 86 00:09:39,430 --> 00:09:45,730 our ViewModel. But I'm getting ahead of myself, back to our code. I'll be 87 00:09:45,730 --> 00:09:51,070 discussing these 2 notify functions later. Remember we still have to make a 88 00:09:51,070 --> 00:09:55,540 change to our ContentProvider, so the RecyclerView shows updates to the 89 00:09:55,540 --> 00:10:03,040 database. Basically, what these calls do is notify any observer - such as the 90 00:10:03,040 --> 00:10:08,530 RecyclerView - that the data from this adapter has changed. If the new cursor 91 00:10:08,530 --> 00:10:15,220 isn't null, then we use notifyDataSetChanged but if it is null then we pass the entire 92 00:10:15,220 --> 00:10:21,910 range of records - that's 0 up to the value of getItemCount - when we call 93 00:10:21,910 --> 00:10:28,060 notifyItemRangeRemoved. This signals that the entire range of records has 94 00:10:28,060 --> 00:10:34,990 gone. And that's our adapter finished, for now anyway. We've still got to deal with 95 00:10:34,990 --> 00:10:38,860 the buttons being tapped, but we'll come back to that once we've got the RecyclerView 96 00:10:38,860 --> 00:10:43,090 working. To get it working, we just have to link it up to this adapter, in 97 00:10:43,090 --> 00:10:46,600 MainActivityFragment. 98 00:10:51,220 --> 00:10:53,340 We'll have to refer to the adapter in several 99 00:10:53,340 --> 00:10:59,060 places in code, so I'll start by declaring a field to store it. 100 00:10:59,060 --> 00:11:04,670 We passed null as the cursor, because we don't have one that's available yet. 101 00:11:04,670 --> 00:11:10,780 Passing null will cause the adapter to return a view containing our instructions, 102 00:11:10,860 --> 00:11:14,780 and that's exactly what we want to happen, when the app starts with 103 00:11:14,780 --> 00:11:19,940 no task records. I'll add the code to attach the adapter to the RecyclerView 104 00:11:19,940 --> 00:11:28,420 in onViewCreated. 105 00:11:28,420 --> 00:11:32,900 Let's set our LayoutManager to a LinearLayoutManager and 106 00:11:32,900 --> 00:11:40,140 attach the adapter. We gave our RecyclerView the ID task_list and 107 00:11:40,140 --> 00:11:46,110 we're referring to it using the synthetic import. You won't see any data 108 00:11:46,110 --> 00:11:51,180 yet, but we can run the app and make sure we haven't broken anything. If all goes 109 00:11:51,180 --> 00:11:56,030 well, we should see the instructions displayed. 110 00:12:03,790 --> 00:12:09,280 okay I'll stop this video here. In the next video we'll write the ViewModel 111 00:12:09,280 --> 00:12:16,770 class that will populate our adapter's cursor. See you in the next video.