1 00:00:00,099 --> 00:00:02,836 (bright music) 2 00:00:02,836 --> 00:00:05,400 (typing) 3 00:00:05,400 --> 00:00:07,200 So we've nearly finished the user interface 4 00:00:07,200 --> 00:00:09,880 for adding and editing the task details. 5 00:00:09,880 --> 00:00:12,680 We've still gotta add the code though to save the data. 6 00:00:12,680 --> 00:00:13,740 Before we do that though, 7 00:00:13,740 --> 00:00:16,520 let's finish the code to display the fragment. 8 00:00:16,520 --> 00:00:19,210 At the moment, the user can't go back to the main screen 9 00:00:19,210 --> 00:00:21,450 without saving the data, which could be annoying 10 00:00:21,450 --> 00:00:24,660 if they've tapped the plus icon by mistake. 11 00:00:24,660 --> 00:00:26,680 We need to provide up navigation 12 00:00:26,680 --> 00:00:28,920 and also decide what we're going to do if the user 13 00:00:28,920 --> 00:00:32,460 taps the back button whilst they're editing a task. 14 00:00:32,460 --> 00:00:35,670 If an activity or fragment allows the user to navigate back, 15 00:00:35,670 --> 00:00:38,970 our users will expect an up button to appear in the toolbar. 16 00:00:38,970 --> 00:00:40,590 Android takes care of that for us 17 00:00:40,590 --> 00:00:43,810 if we pass true to the setDisplayAsHomeUpEnabled 18 00:00:43,810 --> 00:00:45,740 function or the action bar. 19 00:00:45,740 --> 00:00:47,730 Remember from the life cycle videos 20 00:00:47,730 --> 00:00:50,200 that a good place to modify the activity's UI 21 00:00:50,200 --> 00:00:53,900 is the fragment's onActivityCreated function. 22 00:00:53,900 --> 00:00:56,470 When that's called, the fragment knows that the activity's 23 00:00:56,470 --> 00:00:59,920 fully created and that it's layout has been inflated. 24 00:00:59,920 --> 00:01:01,594 So let's make a change there 25 00:01:01,594 --> 00:01:05,190 in our onActivityCreated function. 26 00:01:05,190 --> 00:01:08,810 Specifically we're gonna make a change here on line 49. 27 00:01:08,810 --> 00:01:11,227 I'm gonna type val actionBar equals 28 00:01:14,200 --> 00:01:18,980 parentheses listener as AppCompatActivity 29 00:01:20,360 --> 00:01:24,110 closing parentheses period supportActionBar. 30 00:01:25,683 --> 00:01:28,300 And the next line actionBar question mark 31 00:01:30,147 --> 00:01:33,170 dot setDisplayHomeAsUpEnabled parentheses true 32 00:01:35,470 --> 00:01:36,950 so adding that to the fragment code 33 00:01:36,950 --> 00:01:39,480 rather than setting the up button in the activity 34 00:01:39,480 --> 00:01:41,660 means that we don't have to worry about calling it 35 00:01:41,660 --> 00:01:43,410 whenever we display a new fragment. 36 00:01:43,410 --> 00:01:45,030 So that's pretty straightforward. 37 00:01:45,030 --> 00:01:47,740 We need a reference to the activity's action bar 38 00:01:47,740 --> 00:01:49,280 but it might not have one. 39 00:01:49,280 --> 00:01:51,280 And that's why we're using a safe call 40 00:01:51,280 --> 00:01:53,940 when calling setDisplayHomeAsUpEnabled. 41 00:01:53,940 --> 00:01:56,420 Obviously we can't show the up button in the toolbar 42 00:01:56,420 --> 00:01:57,750 if there isn't a toolbar 43 00:01:57,750 --> 00:01:59,980 but at least now the code won't crash. 44 00:01:59,980 --> 00:02:02,240 What's interesting here thought is the way we obtained 45 00:02:02,240 --> 00:02:05,270 a reference to the support action bar. 46 00:02:05,270 --> 00:02:07,740 This is a very easy mistake to make but it's wrong. 47 00:02:07,740 --> 00:02:10,763 So notice that listener is a nullable type. 48 00:02:10,763 --> 00:02:12,420 OnSaveClicked question mark 49 00:02:12,420 --> 00:02:14,120 so that means that it can be null. 50 00:02:15,340 --> 00:02:17,950 If we scroll up and have a look we can see 51 00:02:17,950 --> 00:02:20,960 we have defined our listener on line 30. 52 00:02:20,960 --> 00:02:22,376 That's in nullable type, 53 00:02:22,376 --> 00:02:24,326 we can see OnSaveClicked, question mark 54 00:02:26,750 --> 00:02:28,550 So that means that it can be null 55 00:02:28,550 --> 00:02:30,620 and we've seen that fragment's activity isn't 56 00:02:30,620 --> 00:02:33,540 always available in the fragment life cycle. 57 00:02:33,540 --> 00:02:36,890 So we're adding this code in the onActivityCreated function 58 00:02:36,890 --> 00:02:40,660 so it's almost certainly safe to do what we've done here. 59 00:02:40,660 --> 00:02:42,340 onActivityCreated won't get called 60 00:02:42,340 --> 00:02:44,990 unless the fragments activity is available. 61 00:02:44,990 --> 00:02:47,900 Even so, I'd advice against doing this. 62 00:02:47,900 --> 00:02:50,650 We've cast listener to appCompatActivity 63 00:02:50,650 --> 00:02:52,780 which it will be if it has an action bar 64 00:02:52,780 --> 00:02:55,700 but we should've specified the nullable type. 65 00:02:55,700 --> 00:02:58,530 In other words appCompactActivity question mark. 66 00:02:58,530 --> 00:03:00,550 As I said though it should be fine in here 67 00:03:00,550 --> 00:03:02,960 we're unlikely to detach from the activity 68 00:03:02,960 --> 00:03:05,470 while onActivityCreated is running. 69 00:03:05,470 --> 00:03:07,400 But the correct code would actually be 70 00:03:07,400 --> 00:03:09,900 to add a question mark after appCompatActivity 71 00:03:12,090 --> 00:03:13,520 and outside of the parentheses 72 00:03:13,520 --> 00:03:15,990 to add a second question mark. 73 00:03:15,990 --> 00:03:18,363 So I'm making one unnullable type making appCompatActivity 74 00:03:18,363 --> 00:03:21,630 a nullable type now and also using a safe call 75 00:03:21,630 --> 00:03:23,780 to support action bar. 76 00:03:23,780 --> 00:03:26,740 So remember that we're showing general techniques here. 77 00:03:26,740 --> 00:03:29,010 Although that would almost certainly have been fine 78 00:03:29,010 --> 00:03:30,106 before I changed it, you may wanna do 79 00:03:30,106 --> 00:03:32,820 something similar somewhere else. 80 00:03:32,820 --> 00:03:35,740 Like inside onActivityCreated in other words. 81 00:03:35,740 --> 00:03:38,650 The thing to take away from this is that Android Studio 82 00:03:38,650 --> 00:03:42,530 and the Kotlin code checker can only help you so much. 83 00:03:42,530 --> 00:03:45,480 If you specify a cast to a non nullable type 84 00:03:45,480 --> 00:03:48,910 then the checker won't warn you to use a safe call. 85 00:03:48,910 --> 00:03:51,340 If you're casting something that could be null 86 00:03:51,340 --> 00:03:53,910 make sure you use a nullable type in the cast 87 00:03:53,910 --> 00:03:57,580 otherwise your code will crash if it attempts to cast null 88 00:03:57,580 --> 00:03:59,120 to any other type. 89 00:03:59,120 --> 00:04:01,730 Technically it might be possible for the checker 90 00:04:01,730 --> 00:04:03,820 to have detected the problem with the cast 91 00:04:03,820 --> 00:04:05,500 so if you get slightly different behaviour 92 00:04:05,500 --> 00:04:07,440 and did see a warning, then that's fine, 93 00:04:07,440 --> 00:04:09,520 things are changing all the time. 94 00:04:09,520 --> 00:04:10,353 Alright so moving on, 95 00:04:10,353 --> 00:04:13,550 next we should really make sure that the cast will succeed. 96 00:04:13,550 --> 00:04:16,380 So let's go ahead and make a change for that. 97 00:04:16,380 --> 00:04:19,120 So I'm going to type if parentheses 98 00:04:19,120 --> 00:04:24,120 listener is AppCompatActivity, closing parentheses 99 00:04:25,608 --> 00:04:27,710 I'm gonna open a code block, left curly brace 100 00:04:27,710 --> 00:04:29,610 and I left the right curly brace below 101 00:04:30,759 --> 00:04:33,770 the set call to setDisplayAsHomeUpEnabled 102 00:04:33,770 --> 00:04:35,840 and then add a line space there. 103 00:04:35,840 --> 00:04:38,760 Now if you use the fragment from a fragment activity 104 00:04:38,760 --> 00:04:41,120 for example it won't crash. 105 00:04:41,120 --> 00:04:43,820 A fragment activity doesn't have to have an action bar 106 00:04:43,820 --> 00:04:46,500 and without this test that code would have crashed. 107 00:04:46,500 --> 00:04:48,410 Because one of the reasons for using a fragment 108 00:04:48,410 --> 00:04:51,050 is so that it can be reused, you shouldn't be making 109 00:04:51,050 --> 00:04:53,770 assumptions about what will be reusing it. 110 00:04:53,770 --> 00:04:56,390 Alright so a brief digression next, 111 00:04:56,390 --> 00:04:58,730 you might be wondering why we can't let Kotlin 112 00:04:58,730 --> 00:05:00,810 perform a safe cast here. 113 00:05:00,810 --> 00:05:03,503 So what I might need is I'm gonna duplicate this line. 114 00:05:05,680 --> 00:05:07,470 I'm gonna comment one out. 115 00:05:07,470 --> 00:05:08,520 So why can't we just do this 116 00:05:08,520 --> 00:05:10,890 so val actionBar equals 117 00:05:12,720 --> 00:05:13,890 I'll just delete the whole, it'll be quicker, 118 00:05:13,890 --> 00:05:15,760 listener.supportActionBar. 119 00:05:18,410 --> 00:05:20,170 Why can't we do that? 120 00:05:20,170 --> 00:05:21,440 Because now we've already checked 121 00:05:21,440 --> 00:05:22,840 that listener isn't null, haven't we? 122 00:05:22,840 --> 00:05:26,470 If it is an appCompatActivity then surely it can't be null? 123 00:05:26,470 --> 00:05:28,380 Well, unfortunately things don't work like that 124 00:05:28,380 --> 00:05:30,320 in a multi-threaded environment 125 00:05:30,320 --> 00:05:31,800 and the error message will confirm that 126 00:05:31,800 --> 00:05:33,270 if I hover over it. 127 00:05:33,270 --> 00:05:35,060 So my cast to appCompatActivity 128 00:05:35,060 --> 00:05:38,510 is impossible because the listener is a mutual property 129 00:05:38,510 --> 00:05:41,200 that could have changed by this time. 130 00:05:41,200 --> 00:05:43,430 We may know that it can't change between our test 131 00:05:43,430 --> 00:05:46,010 and when we get to its supportActionBar, 132 00:05:46,010 --> 00:05:48,460 but there's no way for the compiler to know that. 133 00:05:48,460 --> 00:05:50,160 The compiler isn't aware of the environment 134 00:05:50,160 --> 00:05:51,250 that our code's running in, 135 00:05:51,250 --> 00:05:53,670 it just complies Kotlin code. 136 00:05:53,670 --> 00:05:56,300 Unless it can determine that a smart cast 137 00:05:56,300 --> 00:05:59,010 will always succeed, it won't actually allow one. 138 00:05:59,010 --> 00:06:00,630 And there's been some discussion of this 139 00:06:00,630 --> 00:06:04,190 at kotlinlang.org, and I have a quick read, 140 00:06:04,190 --> 00:06:05,160 I'm just gonna quickly bring that up 141 00:06:05,160 --> 00:06:07,563 on the screen so you've got access to the link. 142 00:06:10,920 --> 00:06:12,580 So, check out this article and have a bit of a read 143 00:06:12,580 --> 00:06:13,870 about that if you're interested in some 144 00:06:13,870 --> 00:06:17,840 of the technical reasons why things work this way. 145 00:06:17,840 --> 00:06:19,790 So, I'm mentioning this now because there's a trick 146 00:06:19,790 --> 00:06:21,900 that we can use to get around the problem, 147 00:06:21,900 --> 00:06:26,540 now go back to our code, so what we can do 148 00:06:26,540 --> 00:06:29,030 is assign listener to a local val 149 00:06:29,030 --> 00:06:30,740 and use that instead. 150 00:06:30,740 --> 00:06:34,260 So, come up here and type val listener 151 00:06:35,290 --> 00:06:36,983 is equal to listener. 152 00:06:38,660 --> 00:06:40,460 So now the compiler can be sure 153 00:06:40,460 --> 00:06:42,300 that the local listener can't be modified 154 00:06:42,300 --> 00:06:46,170 anywhere else and performs the smart cast. 155 00:06:46,170 --> 00:06:48,940 And you can see now that listener on line 52 156 00:06:48,940 --> 00:06:51,750 is highlighted in green to show that a smart cast 157 00:06:51,750 --> 00:06:53,850 is being performed, if you hover over that 158 00:06:55,860 --> 00:06:58,130 we can see confirmation of that. 159 00:06:58,130 --> 00:06:59,890 There's no problem having a local variable 160 00:06:59,890 --> 00:07:02,420 with the same name as the class variable, by the way. 161 00:07:02,420 --> 00:07:05,960 The local variable will be used once its declared. 162 00:07:05,960 --> 00:07:07,370 Alright, so, we're using that technique 163 00:07:07,370 --> 00:07:09,570 a little later, here we're only accessing 164 00:07:09,570 --> 00:07:13,080 a single attribute of the listener in one place 165 00:07:13,080 --> 00:07:15,930 so I prefer to use a nullable type and safe call. 166 00:07:15,930 --> 00:07:17,930 So that only involves typing two question marks 167 00:07:17,930 --> 00:07:19,800 rather than the entire line. 168 00:07:19,800 --> 00:07:21,410 We'll see this technique being used later 169 00:07:21,410 --> 00:07:24,633 but for now that I'm gonna do is undo those changes. 170 00:07:33,271 --> 00:07:34,800 And back to how we had it there. 171 00:07:34,800 --> 00:07:36,070 And again, my preference is to use 172 00:07:36,070 --> 00:07:37,710 a nullable type and safe call, 173 00:07:37,710 --> 00:07:40,100 and as you see that involves typing two question marks 174 00:07:40,100 --> 00:07:42,290 rather than the entire line. 175 00:07:42,290 --> 00:07:44,340 Alright, so the next video, recap of how an object 176 00:07:44,340 --> 00:07:47,400 can be of more than one type at the same time. 177 00:07:47,400 --> 00:07:48,700 See you in the next video.