(bright music)
(typing)
So we've nearly finished the user interface
for adding and editing the task details.
We've still gotta add the code though to save the data.
Before we do that though,
let's finish the code to display the fragment.
At the moment, the user can't go back to the main screen
without saving the data, which could be annoying
if they've tapped the plus icon by mistake.
We need to provide up navigation
and also decide what we're going to do if the user
taps the back button whilst they're editing a task.
If an activity or fragment allows the user to navigate back,
our users will expect an up button to appear in the toolbar.
Android takes care of that for us
if we pass true to the setDisplayAsHomeUpEnabled
function or the action bar.
Remember from the life cycle videos
that a good place to modify the activity's UI
is the fragment's onActivityCreated function.
When that's called, the fragment knows that the activity's
fully created and that it's layout has been inflated.
So let's make a change there
in our onActivityCreated function.
Specifically we're gonna make a change here on line 49.
I'm gonna type val actionBar equals
parentheses listener as AppCompatActivity
closing parentheses period supportActionBar.
And the next line actionBar question mark
dot setDisplayHomeAsUpEnabled parentheses true
so adding that to the fragment code
rather than setting the up button in the activity
means that we don't have to worry about calling it
whenever we display a new fragment.
So that's pretty straightforward.
We need a reference to the activity's action bar
but it might not have one.
And that's why we're using a safe call
when calling setDisplayHomeAsUpEnabled.
Obviously we can't show the up button in the toolbar
if there isn't a toolbar
but at least now the code won't crash.
What's interesting here thought is the way we obtained
a reference to the support action bar.
This is a very easy mistake to make but it's wrong.
So notice that listener is a nullable type.
OnSaveClicked question mark
so that means that it can be null.
If we scroll up and have a look we can see
we have defined our listener on line 30.
That's in nullable type,
we can see OnSaveClicked, question mark
So that means that it can be null
and we've seen that fragment's activity isn't
always available in the fragment life cycle.
So we're adding this code in the onActivityCreated function
so it's almost certainly safe to do what we've done here.
onActivityCreated won't get called
unless the fragments activity is available.
Even so, I'd advice against doing this.
We've cast listener to appCompatActivity
which it will be if it has an action bar
but we should've specified the nullable type.
In other words appCompactActivity question mark.
As I said though it should be fine in here
we're unlikely to detach from the activity
while onActivityCreated is running.
But the correct code would actually be
to add a question mark after appCompatActivity
and outside of the parentheses
to add a second question mark.
So I'm making one unnullable type making appCompatActivity
a nullable type now and also using a safe call
to support action bar.
So remember that we're showing general techniques here.
Although that would almost certainly have been fine
before I changed it, you may wanna do
something similar somewhere else.
Like inside onActivityCreated in other words.
The thing to take away from this is that Android Studio
and the Kotlin code checker can only help you so much.
If you specify a cast to a non nullable type
then the checker won't warn you to use a safe call.
If you're casting something that could be null
make sure you use a nullable type in the cast
otherwise your code will crash if it attempts to cast null
to any other type.
Technically it might be possible for the checker
to have detected the problem with the cast
so if you get slightly different behaviour
and did see a warning, then that's fine,
things are changing all the time.
Alright so moving on,
next we should really make sure that the cast will succeed.
So let's go ahead and make a change for that.
So I'm going to type if parentheses
listener is AppCompatActivity, closing parentheses
I'm gonna open a code block, left curly brace
and I left the right curly brace below
the set call to setDisplayAsHomeUpEnabled
and then add a line space there.
Now if you use the fragment from a fragment activity
for example it won't crash.
A fragment activity doesn't have to have an action bar
and without this test that code would have crashed.
Because one of the reasons for using a fragment
is so that it can be reused, you shouldn't be making
assumptions about what will be reusing it.
Alright so a brief digression next,
you might be wondering why we can't let Kotlin
perform a safe cast here.
So what I might need is I'm gonna duplicate this line.
I'm gonna comment one out.
So why can't we just do this
so val actionBar equals
I'll just delete the whole, it'll be quicker,
listener.supportActionBar.
Why can't we do that?
Because now we've already checked
that listener isn't null, haven't we?
If it is an appCompatActivity then surely it can't be null?
Well, unfortunately things don't work like that
in a multi-threaded environment
and the error message will confirm that
if I hover over it.
So my cast to appCompatActivity
is impossible because the listener is a mutual property
that could have changed by this time.
We may know that it can't change between our test
and when we get to its supportActionBar,
but there's no way for the compiler to know that.
The compiler isn't aware of the environment
that our code's running in,
it just complies Kotlin code.
Unless it can determine that a smart cast
will always succeed, it won't actually allow one.
And there's been some discussion of this
at kotlinlang.org, and I have a quick read,
I'm just gonna quickly bring that up
on the screen so you've got access to the link.
So, check out this article and have a bit of a read
about that if you're interested in some
of the technical reasons why things work this way.
So, I'm mentioning this now because there's a trick
that we can use to get around the problem,
now go back to our code, so what we can do
is assign listener to a local val
and use that instead.
So, come up here and type val listener
is equal to listener.
So now the compiler can be sure
that the local listener can't be modified
anywhere else and performs the smart cast.
And you can see now that listener on line 52
is highlighted in green to show that a smart cast
is being performed, if you hover over that
we can see confirmation of that.
There's no problem having a local variable
with the same name as the class variable, by the way.
The local variable will be used once its declared.
Alright, so, we're using that technique
a little later, here we're only accessing
a single attribute of the listener in one place
so I prefer to use a nullable type and safe call.
So that only involves typing two question marks
rather than the entire line.
We'll see this technique being used later
but for now that I'm gonna do is undo those changes.
And back to how we had it there.
And again, my preference is to use
a nullable type and safe call,
and as you see that involves typing two question marks
rather than the entire line.
Alright, so the next video, recap of how an object
can be of more than one type at the same time.
See you in the next video.