TOMMY MACWILLIAM: In
this video, we're going
to take a look at how to save data
to the user's device from your app.
And to do that, we're
going to be building
an app that lets you take notes.
So you'll be able to browse a
list of notes that you've taken,
create new notes, type something out,
and then persist that, or save it,
to the user's phone.
So if they quit your app and reopen it,
they can have all of their data still
there.
So let's get started.
To persist data to the
user's device, we're
going to use something called SQLite.
So SQLite is a really
simple database that
works by saving everything into
a file with a special format.
And then from that file, you
can run SQL queries on it,
just like you could
on any other database.
So to start, let's quickly take a
look at a few of the different types
of queries that you might write.
The first one is a create query.
So a create query is used to
create a new table in the database.
So when you write a
create query, you're going
to specify the name of the
database you're creating,
the columns in that database,
and the types of each column.
So here's an example of a
SQLite create statement.
We're going to start
by saying create table.
Then we're going to say if not exists.
And that just means, if
there's already a table
there called users, just
don't bother executing this,
because we're already done.
Then the name of the table so, users.
And then in these parentheses
are the names of the columns.
So we have one column
here called id INTEGER,
so that means that the name of the
column is id, the type is INTEGER.
It's our PRIMARY KEY, so that
means it's the unique thing that
identifies each of our rows.
And it's AUTOINCREMENT.
So that just means that every time a
new row is inserted into the table,
we're going to take the id of the
most recent row and add 1 to that.
So this will basically be sequential
as you're adding rows to the database.
Our other column is a text column.
It's called name and
because its type is TEXT,
it allows you to insert any arbitrary
length text and store that as a string.
After you've created a table, you
might want to run an insert query.
So INSERT, as its name suggests, is
going to add rows to a database table.
To write an insert query, we're
going to say INSERT INTO, followed
by the name of our table,
which is users, and then
in parentheses, the names of the columns
that you want to specify values for.
So in this table, we just had one
column called name, so we'll have that.
Then we have the word VALUES.
And then, again, a comma separated
list of all of the values
that we'd like to insert.
So here we're inserting
a single value of Tommy.
After we've inserted rows, we might want
to read them back from the database.
So to do that, we're going
to use a select query.
So we can say SELECT *, which
means select all of the columns
from the table.
If we wanted to, we also could
have given a comma separated list
of columns we wanted to read.
Then we'll say FROM and
the name of the table.
So here the name of our table is users.
And then we'll have a WHERE
clause to filter down the rows.
So we only want to select those
rows where the name column is
exactly equal to the string Tommy.
And so if we just have that
one row in our database,
this will return us that one row.
If there are multiple rows with the name
of Tommy, it will return multiple rows.
We also might want to
update rows in our database.
So we could say UPDATE followed
by the name of that table.
We could say SET name = value.
So we're going to set the name column.
We're going to use the
value of Tommy M. And then
we'll have another WHERE clause to
filter down the rows we want to update,
so we can say where the name
column is equal to Tommy.
So again this is not
going to update all rows.
It'll just update the
rows where clause applies.
So let's jump right into it.
Now that we have some basics
SQL queries ready to go,
let's learn how we can
use them in our app.
So let's open up Android Studio.
And just like we do every time, let's
start a new Android Studio project,
select empty activity, because
we're going to be writing everything
ourselves, click Next.
And the name of our application, we'll
be a little less clever this time
and just call it notes.
Remember package name is
just whatever you want.
We're using the CS50 domain backwards.
Save location, wherever you want.
Make sure language is Java.
Choose your minimum API level.
And make sure use AndroidX is checked.
And now click Finish.
All right, we've seen this
a few times before now.
We know the code that's generated.
Move this over.
And so now, as we did last time,
let's start with the views.
Let's sketch out the views
that our app is going to have.
Then let's take care of
any necessary models.
And then let's write the controller
that wires everything up.
So our app is going to have
two screens, or two activities.
The first one is going
to be a list of all
the notes the user has on their device.
And the second one is going to be
a screen that lets you edit a note
and save it back to a database.
So let's write that
first activity first.
Because it's a list of notes, that says
we should probably use a RecyclerView.
So let's do that.
Let's open up our main activity XML.
And just like we did last time,
we're going to add a RecyclerView.
But before we can do
that, remember we have
to add RecyclerView to our project.
So let's open up build.gradle.
Add as our implementation.
We had androidx.recyclerview.
Name is recyclerview.
And the version was 1.0.0.
And we'll sync this.
So it looks like our
sync was successful,
so now we can use a
RecyclerView in our layout.
So let's come back here.
And just like we did last time,
let's replace this text view
with a RecyclerView.
So we can say androidx.
RecyclerView is going
to autocomplete for us.
We can say match_parent,
again, just like that.
And now, just like before, we have
this full screen RecyclerView.
So now we've added a
RecyclerView to our layout.
Next, we want to create an adapter.
Just like we did last
time, the adapter was
used to manage the data that's
displayed in RecyclerView
and define how that
data could be displayed.
So let's create a new class.
We'll call it NotesAdapter.
No need to specify anything for now.
And we've created a new class.
The other thing we need to
do is create a new layout.
And the layout is going to
define what each row looks like.
So we can actually use a layout that was
pretty much identical to the last one,
so we'll call this our note_row.
Tap OK.
Come over here to text.
And let's just do exactly
what we did last time.
The first thing we did
was we added an ID.
And we called this our note_row.
And then we added a text view.
Again, we had match_parent on the
width and wrap_content on the height.
And then we gave it an ID so
that we could access it later.
And let's just call this note_row_text.
And that's it.
Hopefully, all this
looks really familiar.
It's pretty similar to
what we did last time.
Now let's open up our NoteAdapter class.
And we want to extend
our RecyclerView.Adapter.
And then for the type, we're going to
create a new class in just a minute
called NotesAdapter.NoteViewHolder.
All right, so let's create that class.
public static class NoteViewHolder.
And just like last time, we're going
to extend the RecyclerView holder.
We're going to give this a constructor.
We're going to say NoteViewHolder.
Constructor is going to take some
generic view, call that view.
And then, just like before,
let's add in those fields
for our container and our text view.
So we had a linear layout to
represent our container view.
And a text view--
whoops.
A text view that
represented our text view.
We'll call super on the
constructor to make sure that we
use that super classes constructor.
And then we'll save
references to container view,
so that's going to be view.findViewById.
We had an id of note_row.
And our text view was
view.findById(note_row_text).
So that's it for our view holder.
And now remember there are three
methods that we have to implement.
We have to implement binding a view
holder, creating a view holder,
and getting the number
of items in the list.
So first, let's handle creating views.
So we'll have onCreateViewHolder.
Let autocomplete do a
bunch of stuff here.
And just like before, we're going
to say our LayoutInflator.from
that context object.
Then we're going to
call inflate and we're
going to pass in that layout, R.layout.
This time we called it note_row.
It needs a parent.
And we don't need to worry
about this other behavior,
so we'll just pass false.
And now, we can just return that.
So we're going to return our
NoteViewHolder on that view.
So that's the view that
it's going to hold.
So now we need to think about our data.
The next two methods we need to
implement are binding the view holder
and getting the number of items.
So it's a good time to
think now about our model.
So just like last time,
we know that we want
to create a model that
represents a note.
And each note will probably have
some unique id that makes sense.
And then the contents of a
note can just be a string,
since those can be arbitrarily long.
So let's create a model for our note.
But this time, we're not just going
to create a regular Java object,
we're going to take advantage
of Android's SQLite library.
So first, let's take a look
at the documentation for that.
Android's library for
persistence is called Room.
And it has a nice guide
here on how to use it.
So you can feel free
to read through this
and we're going to walk
through it together.
So the first thing we
need to do is add Room
to our project, which, as you probably
guessed, is just using a Gradle file.
So let's just copy
these two things here.
Again, you want these two
lines to load in everything.
So we'll jump back to our
Gradle file, paste these in.
So for a Room version
here, I'm just going
to use the latest stable
version, which is 2.1.0,
just got that number from
looking at the documentation.
You'll notice here that instead of
implementation, this second line
says annotation processor.
What's going to happen is
we're going to use something
called Java annotations, which is
just like that app override thing
that we added before, that
annotation before a method.
Now these annotations are going to be
used to map fields in some Java object
to columns in a SQLite database.
So basically, to get
access to those annotations
and to make sure the compiler can use
those annotations to generate code,
we just need to add in
this annotation processor.
But I didn't really need
to do anything fancy there.
I just copied that right
from the documentation.
So let's sync our Gradle project.
Everything looks good
there, so we're good to go.
So let's start by creating
a new model class.
So we'll say new Java class.
We'll call this Note.
And press OK.
Now here is what we're going to do a
bunch of magic using the Room library.
So let's define two
fields on this Note class.
Let's call one id.
And that's just an integer.
And let's call the other one contents,
to be the contents of the note.
So now we're going to
add a few annotations
provided to us from that Room library.
So for this first field, we're going
to add the annotation PrimaryKey.
You can see this was imported
by this Room library.
And for the second field, we're going to
add this annotation called ColumnInfo.
And this specifies that this is
another column in our database.
Now annotations can also take arguments.
And so this one takes
an argument called name.
And we're just going
to name it contents.
It's the same thing.
If you had a column name that
was different than this field,
that's where you could
specify that mapping.
Now the last annotation we want
to add is this entity annotation.
And so this says that this class
represents a table in my database
and with its arguments I can specify
the name of my table is notes.
So this will create a SQLite table
called the notes with two columns,
a primary key integer
autoincrement column called id,
and a string column, which corresponds
to a text column in SQLite3,
called contents.
And so now, this is really cool, we're
just mapping each row in this database
automatically to one
of these model objects.
And that's really going to
simplify the amount of code
we have to write in order to read
and write from this database.
So now that we have
our model class done,
let's go ahead and use
that in the adapter.
Just like we did last time,
let's create a list of note.
We'll call it notes.
And initially, let's just
have it be an empty list.
So now we can implement that
onBindViewHolder method.
So just like we did before, we can use
this position parameter to get a note.
So we'll say Note current
is notes.getPosition.
And so now we just want to
set the text of this text view
to be the text of the note.
So let's say I have my holder.textView.
And we'll set the text
to be current.contents.
Pretty simple.
Lastly, let's define a method to get the
number of notes inside of the adapter.
So that's going to be my getItemCount.
And just like before, let's
just return notes.size.
OK, so this is a pretty
simple version of the adapter.
It doesn't do too much yet,
but it's some good boilerplate.
And I have this red line here.
Forgot to say new.
So that's going to fix
that compilation error.
So now, just like we did last time,
let's add the necessary classes
to our main activity.
So we need a few things.
The first thing we
need is a RecyclerView.
And we can just call that RecyclerView.
The next thing we need is an adapter.
So that can be our notes adapter.
And the last thing we need
is that layout manager.
Now inside of onCreate, we're going
to grab and instantiate these objects.
So RecyclerView is going
to be findViewById.
Looks like we forgot to give
that an id, so we can come back.
Autocomplete let me know I forgot.
And so I'll just say
android: id is RecyclerView.
So now autocomplete
should be good and it is.
That's our RecyclerView.
Remember layout manager, we can
just say is a linear layout manager.
Give it access to some activity.
And then our notes adapter, it didn't
take any arguments in the constructor.
So we can just say new NotesAdapter.
Finally, we can set these values.
So we can say
recyclerView.setLayoutManager
and recyclerView.setAdapter.
And so all this should
look really familiar.
We're just grabbing that
RecyclerView from the layout.
And we're configuring its layout
manager, just laying things out
straight in the line.
And we're configuring its adapter,
which is a new instance of that adapter
that we just wrote.
So right now, if we were to run this, it
wouldn't be a terribly interesting app,
because we know our RecyclerView
is going to be empty.
So now, let's go ahead and take care
of writing all of the model code
that's necessary to do three things.
The first is creating a new note.
The second is getting all the notes
that a user has already created.
And the third is saving a note to disk.
To do that, we're going to
write a new class called a DAO,
or it stands for data access object.
And again, we're going to use this
Room library that Android provides
to really easily map methods
to queries that we're
going to run on the database.
So let's do that.
Let's create a new class to
represent our data access object.
So we're going to say a new Java class
and we're going to call this a NoteDao.
Click OK.
So if you read through the Room
documentation, what you'll find
is the way that this works is
we're going to write, not a class,
but actually an interface.
And we're just going
to annotate the methods
in this interface with some queries.
And then under the hood, the Room
library, while everything is compiling,
is going to generate
some classes for us.
So essentially, the compiler is writing
some code for us so we don't have to.
So to start, let's change
this class to an interface
and add the first
annotation, which is a DAO.
So we've specified this is
a Room data access object.
And so now the Room library is going
to do something with this interface.
First, let's specify a
method for creating a note.
Now we don't need to pass
any arguments to this method,
because we're just going to create
a note with some default values.
So now to specify the
SQL query that's going
to be run when you have this create
object, we're going to specify @query.
Again, this is another
annotation from the Room library.
And here is where we're
going to write our SQL.
So we're going to say
INSERT INTO notes--
just need the contents--
VALUES, we'll just say "new note."
And that's the default
value for one of our notes.
So now what's happening here is
we're going to generate some code.
And whenever this
create method is called,
this SQL query is going
to be run on our database.
So let's do the same thing to
implement those two other methods.
The next one we can
implement is the method
to get all of the notes in the database.
So that's going to
return a list of notes.
And we'll call this method getAllNotes.
And again let's use
this @Query annotation
to specify the SQL query we want to run.
So we want to say SELECT *, just
grab everything, FROM notes.
And that's it.
That's a SQL query that's going to say,
give me all of the rows in the database
and grab all of the
columns for those rows.
So that was pretty easy.
So last, let's write the method
to save notes to our database.
So return type will just be void,
because we're not returning anything.
We're just writing something to disk.
And let's call this method save.
Now this method, unlike the other
two, requires a couple parameters.
Let's call one contents.
Let's call the other id.
And this makes sense, because in order
to save something to the database,
we need to pass in the
value to save, then also
the id of the note that
we want to save it to.
So we can actually use the
values of these parameters inside
of our annotation.
So we'll say @Query and then
we'll write our update query.
We're going to say UPDATE notes SET
contents = :contents WHERE id = :id.
So this is taking advantage of a SQLite
feature called parameter binding.
So this says, wherever
I see that :contents,
I'm going to replace that with the value
of the variable that's inside of this
method declaration.
And same with id.
So I don't really have to do any
kind of fancy string interpolation.
And this is also a much safer way of
getting user input into the database.
If you're just manually
concatenating strings,
you're opening yourself up to
different types of attacks.
And so this is just a really
safe way of making sure
that you take some user input,
like the contents of a note,
and use it in the context
of a SQLite query.
So this is it.
This is actually our
entire data access object.
Interestingly, we didn't have
to write any lines of code
to manually save
something to the database
or open a file or anything like that.
We also didn't need to write any code
to convert some kind of row object
to some kind of note object.
All of that is taken care
of by this Room library.
What we did have to
write, though, was the SQL
that we want to be executed when
you call these various methods.
So that's it for our Note DOA.
We have to write one more
class in order to use this.
And that class is
called a database class.
So just as we did before,
let's create a new class.
Let's call this our NoteDatabase.
In here, as you probably
guessed, we're going
to use some more annotations
provided to us by the Room library.
So the annotation we're going to use for
this class is the database annotation.
So the database annotation
specifies that this
is the database, or sort of the entry
point for all of the data access
objects that your app is going to use.
So we can see here from our
autocomplete that this annotation
takes one argument called entities.
And here, we can specify a list
of all of the data access objects
that we need to use.
So we just have one,
which is our NoteDao,
and we're going to use this
.class syntax to indicate that
we're specifying and passing in a class.
Second argument.
So this annotation is a version number.
Let's say you release an app and you're
sort of changing the database scheme
and you want to version it, you
can just specify a version here
to let Room know when you have
a different database schema.
OK, so that looks pretty good.
Now we're saying that
inside of this database,
we have this one data access object
that we can use to get different values.
So now let's move on to this class.
The first thing we want to do is
extend our Room database class.
And so this is a base class that's going
to specify a bunch of different methods
for us.
And one other thing we want to do is
add this keyword abstract to this class.
So an abstract class is somewhere
between a class and an interface.
In an abstract class,
you can have some methods
that have bodies and
definitions, and you can also
have some methods that
don't have any bodies.
And when you extend
an abstract class, you
have access to all of those methods
that are defined and have a body,
and you're required to implement
these other methods that
are marked as abstract.
So you can think about it
as sort of a combination.
It's like an interface that
requires you to sign this contract
to implement some set of
methods, but like a class,
it also has some functionality already
so you can call some other classes.
So by saying abstract
in front of the method,
that's how you indicate
that this isn't something
that I'm going to implant myself.
Instead, this is something that's going
to be implemented by somebody else.
So we're going to add one
abstract method to this class.
We're going to say public abstract
NoteDao and we'll just call it noteDao.
And so the reason that
this is abstract is
because, again, this Room library,
when you're compiling these files,
it's going to generate some code for us.
And so the class that
implements, or extends,
this abstract class is going to
be generated by the Room library.
And with this @database
annotation, that's
what's going to tell the
compiler, lets go and generate
some code that can be used.
So we're not actually going to have
to implement any of this ourself.
It's this Room library that's
writing the code for us.
So now, that's all of the model
code we're going to have to write.
Let's just quickly recap what we did.
First, we defined our note class.
In our note class is where we
define what our data looks like.
We said we have a table
for notes and each entry
in that table has two fields.
One of it is a unique
identifier that's an integer.
And the other is a string that
contains some contents about the note.
So this defined our table.
After we defined our
table, we defined a way
to access that table and sort
of interact with the table.
And we defined three methods to do that.
We defined a method that runs in insert
query to add values to the table.
We defined a method that runs a select
query to get data back from the table.
And we defined an update method to
save data that's inside of the table.
Last, we created this database
class that basically collects all
of the data access objects together.
In our case, we only have one and so
we listed it here in our annotation.
And then we created one abstract
method that someone else
is going to implement for us to get
access to an instance of that data
access object.
So now that all of our
model code is written,
let's take a look at how we can use
that model code in our application.
So first, let's jump back
to that main activity.
So first, let's define an
object that's going to represent
a connection to this database.
So I'm going to create a new
static field on my main activity.
Its type is NotesDatabase.
And I'm going to call it database.
And the reason I'm making this static
is because I want all the different
activities in my app-- in this
case, there are only two--
to use the same instance of a database.
And rather than passing that
around constructors everywhere,
I can just make this static and then
other classes in my app can use it.
So once I've defined the field,
let's set its value to something.
So I'm going to say
Room.databaseBuilder.
And this takes a couple
arguments, it looks like.
The first argument is a context.
And we saw before that there's just
some global context object we can use.
The next argument is going to be
the class name for our database.
And that's our NotesDatabase.class.
And finally, we want a name.
So that's sort of a string
name for our database
and that's the file that
we're going to save on disk.
Let's just call that notes.
The next thing we want
to do is just allow
our database to run on the foreground.
So normally, you'd want it to
sort of do heavy database queries
in some kind of background task.
We're not going to worry about that.
So let's just, say, allow this to happen
on this sort of main foreground task.
And finally, we're going to call build.
And so this is going to give us
back a database that we can use.
So let's use this new object.
Let's jump over to our NotesAdapter.
And let's create a new
method on this adapter
to load everything from the database.
So we'll just call this reload.
So inside of our reload
method, let's first set
the value of that notes field.
And so we can say MainActivity.database.
Remember, we made
database a static field,
which means that we can access it by
referencing this MainActivity class.
So say MainActivity.database dot
note data access object, or noteDao,
and that's a method that we defined.
And then we can say, getAllNotes.
And this is going to call
that method from the NoteDao,
run that select query, and
give us back a list of notes.
Now just like last time, we have
to call notifiyDataSetChanged.
And that's going to
tell the RecyclerView
that we've just updated that
underlying object, reload yourself.
So now that we've written
this method, let's use it.
Let's come back to our onCreate
and call adapter.reload.
And this says, when the app runs,
the first thing it's going to do
is go to the database, grab everything,
and display it in the RecyclerView.
View.
So now we have a mechanism for
reading notes from the database,
but we don't have a way
to create new notes.
And if you remember our UI doesn't
have any controls or buttons
to do that either.
So let's add a button to this
activity that, when we press it,
is going to create a new note.
And the class that I'm
going to use is this class
that's given to us by Android
called a floating action button.
This is a pretty common Android pattern.
You've often seen, you know, a
button sort of on top of a list
and you can tap that button
and something happens.
So that button is defined
in, again, a separate library
that we need to pull
in, so let's do that.
And the name of this library
is com.google.android.material.
So the name of the library is
Material and the version is 1.0.0.
So this is just another class
it's provided to us by Android.
And it contains a bunch of other
UI elements we might want to use
and so let's just pull
those into our project.
So now that we've downloaded this
library of material UI components,
let's use them in our app.
So let's jump back to the
layout for our main activity.
So the first thing we want to do is
change the layout that's used here.
And we want to pick
a layout that's going
to enable that floating
action button to sort of float
on top of the RecyclerView.
So we're going to change
ConstraintLayout to something else.
We're going to use what's
called a CoordinatorLayout.
And this is just basically a
different type of layout that's
going to allow us to do what we want.
So after we've changed that,
let's come back over to here
and add a floating action button.
So just like before, let's
specify a few of those attributes.
So we have our layout width, and let's
just have these both be wrap_content.
So that's our width.
That's our height.
Let's give this button an
ID, just so we have it.
We have @+id and we have, let's
just call this, add_note_button.
After we give it an ID, let's
specify where we want it to float.
So let's add our button to the
bottom right of the screen,
since that's kind of a
common place to put it.
So let's say android: layout_gravity
is going to be in the bottom(right).
And then we don't want it to be
all the way on the bottom right,
so let's just give it a
little bit of padding.
So we'll say android: layout_margin.
And let's just say 16 pixels.
That probably looks pretty good.
So the next thing we want to
add is an icon to our button.
And luckily, Android provides
a bunch of default icons
that we can use so we don't have to
add our own or create our own icon.
To do that, I'm going to
use this app: srcCompat
And I'm going to start
by saying @android.
And then here, you can see there's
a bunch of stuff supplied already.
So if I start typing drawable,
I can see a bunch of icons.
So I'm going to use this input_add icon.
And so this is just a little plus.
It basically says this
is the system icon
that you should use if you're
having a plus button or button that
adds something.
And we do, because we're adding a note.
Last, let's give our button a color.
So we're going to say android: tint.
And you can start by saying @color.
And again, there's a bunch of
colors here that are listed.
I'm just going to pick this
one, sort of a background color
for a light background.
So that's everything with
our floating action button.
So now, we've styled this pretty nicely.
So now, let's build our application
and make sure there are no errors.
So let's come up here to build.
Yup, looks like we have an error.
So let's jump over here.
Looks like there's some kind
of problem with our database.
So let's come over here.
And, yeah, here, so rather than
doing the data access object,
we want to use the entity.
So recall that Note is what we
decorated with the @entity annotation.
So we just want to make sure that that's
the note, not the note data access
object.
So now if we rebuild, now
we've compiled successfully.
So let's run the app and just take
a look at what this floating action
button looks like.
Looks like we've installed successfully.
So let's pull up our emulator.
That looks pretty nice.
So we have a RecyclerView here.
It's empty, because we
haven't created any notes yet.
Now we have this nice button
over here that, when we press it,
doesn't do anything.
So let's hook up an action to
this floating action button.
To add an action, let's jump
back to our MainActivity.
And in onCreate, let's grab
that floating action button.
So we'll create a button object.
And we'll set this
equal to findViewById.
And we called this add_note_button.
So with this button object, we can
call that same method we called before,
which was setOnClickListener.
And this took one argument,
which was a view.onClickListener.
See if we can get the
auto complete here.
There we go.
And so remember, we're just overwriting
this one method called onClick.
And so when we click this
button, we want to do two things.
The first thing we want to
do is create a new note.
So let's get that note data access
object just like we did last time.
Say database.noteDao.
And this time, we're
going to call create.
And that's going to create a new note.
Next, let's reload the adapter.
So let's call adapter.reload.
And that's going to tell the adapter
that now that we've created a new note,
let's refresh the RecyclerView
so that we can see it in action.
So let's try this out.
Let's run this app.
Here's our emulator.
Let's give our action button a click.
And there we go.
Here's a row that
represents our new note.
The contents of this
row are the contents
of the note, which is just new note.
So we're off to a good start.
Next, let's hook up a new activity to
display the full contents of a note
and enable the user to change it.
So remember to do that, we're going
to come over here to the left,
we're going to say new
activity, and we're just
going to create an empty activity.
And we're going to call
this are NoteActivity.
All right, so that created
a Java class and a layout.
Let's go to the layout first.
So here, let's just create
a really simple EditText.
So EditText is actually
a subclass of TextView.
And as its name suggests,
it's editable, instead of just
being a read-only string of text.
So let's just set this to be
the entire width of the screen.
So we're just going to
match_parent on both of those.
And then let's set an ID,
because we know that we're
going to need to access that later.
So we'll call this note_edit_text.
And that's it.
So now, let's open up that new class
that was generated, this NoteActivity.
As we always do, let's create
a field here for this EditText.
And let's grab a hold of it in onCreate.
OK.
So we want to do two things.
When this activity is created, we
want to set the text of this EditText
to be the note that's in the database.
So that way, when we open
this up for the first time,
the user just sees
what was there before.
The other thing we want to do is
when the user leaves this activity,
we want to save the current contents
of that EditText back to the database.
So let's do those two things.
So now that we have a reference to this
EditText, let's set the contents to be
the contents of the
note the user selected.
To do that, we're going to do
just what we did last time.
We're going to use an intent
to pass the contents from one
activity to the other.
So let's assume that our intent has
defined something like contents.
We'll say String contents is get
getIntent().getStringExtra() called
contents.
So now we'll just set the text of
the EditText to be those contents.
So that's loading the note.
And the other thing we want
to do is to save the note.
So to do that, we're going to use
another method called onPause.
So where onCreate is called when
the activity is first created,
onPause it's going to be called
when you leave the activity.
So just as before, let's
make sure we call super.
And so now we want to use that database
NotesDao to save the contents to disk.
So let's just save the id of the
note that this activity represents
in a field.
So we'll say private int id.
And let's just use the
intent to store that,
so we'll say id is
getIntent().getIntExtra().
And we'll call that id and we'll
just give it a default value of 0.
Now in onPause, we can use
that same database object.
So we'll say
MainActivity.database.noteDao().
And now we're going to call save.
And remember that save
takes two arguments.
The first one is the
contents of the note.
The second one is the id of the note.
Contents of the note, that's going
to be from the current text field.
So that's going to be editText.getText()
is going to get me the text
that's currently in there.
And then we just saved the is, so
I can just call id right there.
So now this says, before
this activity is closed,
when the user hits that back
button, let's persist everything
that we have to disk.
So now, let's write the other
half of this interaction.
So let's come back to our notes adapter.
And just like we did last time,
we want to attach a click listener
to the container.
So we're going to say
containerView.setOnClickListener.
Let the autocomplete
generate that for us.
And so this is going to
feel pretty familiar.
We want to create an intent.
We want to set the id and the
contents extras field of the intent.
And then we want to start the activity.
So we need to make sure that this view
holder has a reference to the note.
So to do that, remember
what we did last time.
We said holder.containerView
and we used this setTag method.
So we'll pass along the
current note right there.
Now, we can say my note--
current note is going to
be my containerView.getTag.
Remember we just have
to cast this to a note,
because this is just a
regular object by default.
OK, so now let's create our intent.
Remember the way to do
that is by creating a new--
first make sure we're importing it.
Create a new intent.
And the intent is going to
take a couple of arguments.
The first one is that context
object and the second one
is the class that we want to go use.
That's going to be NoteActivity.class.
Now using that note object,
let's put in those extras.
Let's say Intent.putExtra().
We have the id of our note and
that's going to be current.id.
And we have the contents of the note
and that's going to be current.contents.
Lastly, let's just start the activity.
So we're going to say
getContext.startActivity,
pass along that intent.
So now when the user
taps that row, they're
going to see the contents of the note.
Last but not least, we have to make sure
we're reloading the notes every time
the user comes back.
Because remember, that notes list is
displaying a preview of the note text.
We want to refresh that every time
a user finishes editing a note.
So to do that, we're going
to use a third method.
We've seen onCreate
and we've seen onPause.
We're going to use one
more called onResume.
So onResume is going to be
called every time an activity is
brought to the foreground.
So when the activity is first
created, it's going to call onCreate.
And then it's going to call
onResume, because the activity just
entered the foreground.
Then we could jump off to some
other activity and come back
and then onResume is
going to be called again,
because we're back in the foreground.
So this is a great method
to reload our RecyclerView.
So rather than reloading the
RecyclerView here in onCreate,
we're just going to move
this down to onResume.
And so this will both reload
when the app starts up
and every time the user comes back.
So now let's give this a shot.
Let's run our application.
OK, looks like we have one error here.
So it looks like when we call getText,
it returns this editable object.
So we forgot to just
change that to a string.
Luckily, that's really easy.
We can just say .toString.
So let's try running this again.
Looks like we built successfully.
So here's that RecyclerView again.
Let's tap on a note.
And OK, it looks like we got a crash.
So let's take a look at our
log and see what happened.
So we open up our log and we can see
here in red, attempt to read from field
on a null object reference.
OK, so it even told us it's
in NotesAdapter, line 31.
So I can actually just
click that and it's
going to jump me right to
where my exception happened.
So it looks like here current is null.
Now let's think about this.
So it looks like we're setting
current in the constructor,
but that's not right, because when
we invoke the constructor, as up
here in onCreate view holder.
But we're actually setting
the tag in onBindViewHolder.
So instead of having this
getTag here, we actually
want to put it inside of our onClick.
So now let's try
rerunning the app again.
OK, we compiled successfully.
Let's try again and tap on this row.
And great, so looks like we've
loaded this note from our database.
So that worked out.
Let's try testing it
out by saving the note.
So I'm going to--
this is a test note.
OK, I'm going to use the back
button to come back to my list.
And great, it looks like
we've saved it to the database
and we've successfully reloaded
it in our RecyclerView.
So before we finish up, let's just add
some last finishing touches to make
our app look a little bit nicer.
Let's start with our note activity.
So there are a couple of things I
wanted to change about that edit text.
First is it was kind of weird
that the text was centered,
so let's move the gravity of
that text to be at the top.
So we're going to set the
android: gravity to top.
And so now rather than
centering that text,
that's going to put the text
at the top of the edit text.
Let's also add a little bit
of padding, just so the text
doesn't run up against the screen.
So let's say padding.
Let's use 10 pixels for that.
Lastly, we have that weird kind of pink
bar at the bottom of the edit text.
We can get rid of that just by
setting the background property.
So let's set at @android
background to be
equal to this @android
color/transparent.
And so that's going to make sure
that we have that transparency.
OK, so now that our second
activity looks a bit nicer.
Let's come back to that first
activity and apply those same changes
from our other app.
So we're going to set
a couple of properties.
One is going to be make
sure it's clickable.
So we'll set clickable
flexible to be true.
Let's space out the rows a little bit.
We'll say android: padding is 10 pixels.
And lastly, let's add
that nice ripple effect,
which was foreground
and then you can say
android_attr/selectableitembackground.
And so this will just make that
RecyclerView look a bit nicer again.
So let's take a look at
these aesthetic changes
by running the app one more time.
All right, let's pull up the emulator.
So now a row is looking a bit nicer.
There's some more padding.
If I tap this, I get
that nice ripple effect.
And then that kind of
weird background is gone.
So that's it for our notes app.
To recap, we've made an app,
used a RecyclerView to display
a list of contents from
our database, and then we
wrote our DOA to save
notes, to create notes,
and to get them all from the database.
And so now, you can write Android
apps to save settings and other things
to the user's phone, so they can have
them when they open the app again.
