(gentle, futuristic music)
(keyboard typing)
Right, so we've got
the user interface working well now,
so it's time to get our app to save the data.
In the life cycle videos, I mentioned that you may
prefer to save the data automatically
in the onPause or onStop functions.
That's the decision you make when designing your apps,
and will probably be a good decision for this one.
We're going to use the save button instead.
We're doing that so we can take a look at using dialogues,
which wouldn't be appropriate in
the onPause or onStop functions.
Both of those functions should
finish as quickly as possible,
and popping up a dialogue isn't a good idea
from these life cycle functions.
Interrupting the life cycle while your app
waits for a user to dismiss the dialogue isn't a good thing
to do, in fact, it's a very bad thing to do.
When you've seen the code to save the task data,
then by all means move it into onPause or onStop,
for now we'll put it in the save button's onClickListener,
so that we can cover using dialogues in this app.
Before we can save the details, we need to populate
our edit text widgets with the task details.
Remember that this fragment's being used to add a new task,
as well as edit existing task details.
If the argument passed to the fragment is null,
then we're adding a new task, otherwise the argument
will be the task that we're editing.
I've already mentioned that we can't change
UI elements in the onCreate function,
because our UI doesn't exist at that point.
It's not inflated until onCreateView.
A good place to modify the UI is
in the onViewCreated function.
When that gets called, we know our layout's been inflated,
and all the views exist.
We're already overriding onViewCreate in our
AddEditFragment, and I'll just bring that on the screen.
And we're overriding onViewCreated
so that we could just see it being called.
I could leave it where it is,
and it's right down towards the bottom of the file here.
There it is, onViewCreated, but I prefer to have the life
cycle functions appearing in the order they get called.
All the life cycle functions that we've added
were just there so we could log them,
and we'll leave them still at the
end of the AddEditFragment file,
but I'll move the ones we really want up as we use them.
So for this one, we're going
to cut the onViewCreated function
where it is, and what I'm going to do is paste it
just after the onCreateView function, just here.
Okay.
Right, so in terms of what we want to do here,
if a task exists, we'll set its values into the widgets,
we'll also log what we're doing to make debugging easier.
Right, so in terms of code, we don't need the call to
the super function, so I'm going to delete that line,
but how do I know that?
Well if we CMD or CTRL click that,
and we're checking out the source code,
and as you can see, the function body's empty,
so we know that the super function
is not actually doing anything.
OnViewCreated is called after onCreateView,
but, and this is the interesting bit,
it's called before any save state
has been restored to the view.
That's important, because we're about
to populate the edit text widgets with the
data from the task to be edited.
If the user rotates the device,
these life cycle methods get called again,
and we'll be resetting the widgets to the values they had
when the fragment was first created.
But if we do that after state is restored,
we'd be replacing any edits that the users made.
That wouldn't be good, if all their changes
got wiped out because they rotated their phone.
Because state hasn't been restored yet,
the values we're about to put into the widgets
will be replaced with any text that the users have typed.
So we'll go back to AddEditFragment,
close down this fragment source code.
So we'll actually delete that line and then add the code
and we'll come back to that point shortly.
I can start by typing if task, and task is in parentheses,
is not equal to null, I put a code block,
and then we're going to do a Log.d parentheses
TAG comma, double quotes, onViewCreated,
colon space, Task details found,
comma, editing task, $,
left and right curly braces and we'll go with task.id.
And we're going to ignore the
error that you see there for task,
and we'll get a few of those errors as we type this in,
and I'll explain that a little bit later in the video.
Alright, next line, addedit_name.setText,
we're setting the text for the widget,
and it's going to be task.name.
Addedit_description.setText,
parentheses for that, will be
task.description in parentheses.
Then the third line,
addedit_sortorder.setText again,
parentheses, and within the parentheses, Integer.toString,
and in parentheses, it'll be task.sortOrder.
Alright, so that's the code, if task isn't null,
else, the code block for else will be,
we'll just put a comment here so we know what we're doing.
So basically no task, so we must be adding a new task,
and not editing an existing one.
So we'll just add a log at this stage,
Log.d, parentheses TAG comma,
double quotes, onViewCreated,
colon No arguments,
adding new record.
Alright, so that's pretty straightforward and simple code.
We're just displaying the task details if there is a task.
It's a bit of a waste of time doing that if the details
are only going to be replaced by the user's edits.
Fortunately, we know about the fragment life cycle
and we know that the savedInstanceState argument
for this method will be null when
the fragment's first created.
Then after that, after our rotation for example,
the savedInstanceState bundle will contain
the data from the edit text widgets.
We can check that and then only populate
the widgets ourselves if that's necessary.
So let's add the code to do that.
We want to surround our other code,
so if parentheses, and it's going to be
savedInstanceState is not equal to null,
closing parentheses, at the left curly brace,
I'm going to add the right curly brace down here
right just before the end of the function,
we should indent everything nicely.
Alright, so I've now wrapped that in that test
to see whether savedInstanceState is not equal to null.
And in fact, that should have been
if savedInstanceState is equal to null,
because again, what we're trying to do,
we know that savedInstanceState argument will be null
when the fragment's first created.
So what we're doing a test for here
is if it is null, in other words if it's first created,
then we're grabbing the data
and populating the various widgets.
And this also saves processing, which is
kinder on our user's batteries.
Alright, so we've got those errors that I mentioned
when I referred to task in the code.
This is a more extreme example of the problem
I referred to in a previous video,
when we tried to cast the listener to AppCompatActivity,
and the error is if I go over any of these references.
Smart cast to Task is impossible because Task
is a mutable property that could
have been changed by this time.
Now we've just tested for null
and we know that Task won't change.
The Kotlin compiler doesn't know that, though.
Kotlin's been designed to be a very safe language,
and unless the compiler performed a thorough analysis of all
the code, including all the code in the Android framework,
there's no way it can tell that we're doing something safe.
The Kotlin designers aren't going
to add that sort of analysis to the compiler, it would
increase compile time very significantly, for one thing.
So it's up to us to deal with situations like this.
So we could use the bang bang operator.
We know that Task might be modified, so that's one approach.
So if we come up here and add the bang bang operator,
that clears the error and is one approach.
We would have to do that for every line
that the task appears in, so I prefer a different solution,
and that's the solution I mentioned in an earlier video.
Basically creating a local val variable
and assigning Task to that.
So let's do that, within the test for savedInstanceState
being equal to null, val task equals task.
So now the code block's referring to that
local variable, not to the Task argument.
Now we've also got a warning though over here now.
From Android Studio, that the null,
and we'll just go over it, hover over it.
Basically unnecessary non-null assertion
on a non-null receiver of type Task.
So we can just remove the bang bang operator now,
and that fixes that warning.
But I've also got another warning here
on line 54, about number formatting, and specifically,
it doesn't take into account locale settings.
If we were dealing with numbers that had decimal points,
we would certainly take that warning seriously.
Europe, for example, uses a comma
instead of a dot for the decimal point,
and a dot instead of a comma for the thousand separator.
But here we're only dealing with integer values,
without thousand separators, and there's
no need to consider localization in this particular case.
The easiest way to remove that warning
is just to use the light bulb to suppress it.
So depending on where you are on the line,
we should be able to click into there,
and the light bulb will appear,
but it's very much depending on where you're clicking
before we get the right way to suppress it.
So I can click into the left parentheses,
is usually a good option, and that now works.
We've now got the suppress message come up,
and we can click on that, and we get, on line 47,
an annotation above the onViewCreated function,
and that warning has now disappeared as a result.
Alright, so that's the first part of the UI done.
In the next video, we will need to
add the code to the save button's listener
to save the details to the database.
See you in the next video.