Alright, so this adapter's
going to use a cursor as the source of
the data to be displayed.
So we need a field to hold the cursor.
So I'm gonna add that to the class constructor.
What we have to do is specify
the nullable type cursor question mark
because the cursor, our cursor,
will be null until we've downloaded the data.
So let's have a go at doing that.
So we're gonna add in parentheses after the class name,
private
var
cursor:
and Cursor? as I mentioned.
Let's now edit that cursor to the constructor.
Alright, so there's three methods we have to implement
to satisfy the RecyclerViewAdapter contract.
Now I've mentioned before that an interface is a contract
and if you implement an interface
then you have to provide an implementation
of all the methods that the interface defines.
The same is true when you extend an abstract class,
your class has to provide an implementation
of all the methods defined in the abstract-based class
that you're extending.
So, what I'll do here,
is I'll get Android Studio to generate
the methods we need to implement.
Just using Control + I and that's obviously
with a cursor already inside the class bodies.
You can see I've got.
Now select all three.
Just press Enter there to create them.
Now in terms of these methods,
I prefer to have the onBindViewHolder function
appear immediately after the onCreateViewHolder.
So, if your codes generated in a different order as mine is,
just cut and paste to rearrange it.
I'm going to do that now.
Personal preference of where it should be.
It's not an issue though,
as far as the compile is concerned,
but it makes it easier,
if you want to compare your code to mine, ultimately.
Also, I take this opportunity
to change the parameter names for these first two functions.
Android Studio sometimes just gives them the names,
p0, p1, etc.,
which is done here,
when that's not really very informative.
Alright, starting off with on line 25.
We'll change p0, that should be parent,
for first argument.
And the second one is the viewType.
Alright, and then for the second method on line 29,
we're going to go with holder, instead of p0
for the argument name.
And, for the second one, it's gonna be position.
I think that's a bit more informative
and makes more sense now reading.
And with same as three methods before,
onCreativeViewHolder is called by the RecyclerView
when it needs a new view to display.
The flicker browser app displayed
the same details in each line in the RecyclerView
and this app does the same.
But the RecyclerView can be used to display
different things of each row.
You give it a valid view
and it will display it at the correct position.
And, that's why there's a viewType parameter,
so your function can respond to the type that's requested
and return different views, accordingly.
Best example of that, think about ChatApp.
The views for the incoming message
may have the text to appear to the left,
while the messages being sent from the device
have them on the right.
Now that's a bit simplified
and you'd probably implement that
by starting the widgets before sending the view back,
but how about if an incoming message contained a file?
You may have in return a different view
that included an image view widget to display the picture.
If the file type wasn't a recognised type of image,
you can add a button to download the file instead.
So, this involves implementing getArt and viewType,
so that you can switch
the type of view at any given position.
But, we display in the same kind of data
throughout the RecyclerView list,
so we can safely ignore that viewType argument.
And we're not gonna be displaying
different types of views in our list,
but if you want to do that,
then you know now how to search for something like
RecyclerView, getArt, and viewType.
Alright, so our onCreateViewHolder
function will just inflate a view
from the task_list_items layout
that we created earlier and then return that view.
So, let's write some code for that.
We're gonna delete the TODO
and we'll start with
a Log.d,
in parentheses, TAG and double quote,
onCreateViewHolder,
because that's the functionary,
and we'll put new view requested,
so we can see that in our log.
Alright, so the code next line is val view =,
it's gonna be LayoutInflater
It's gonna be .from,
in parentheses; parent.context,
closing parenthesis, .inflate,
and parenthesis again,
R.layout.task_list_items,
and then ,parent, false
and then we got a closing raw parenthesis on the end there,
which we can see down there.
Alright, then the last line we're going to return
a new TaskViewHolder object.
The TaskViewHolder,
in parenthesis will be view that we created
on the previous line.
So, that's onCreateViewHolder
and again that's just inflating a view
from the task_list_items layout,
that we created earlier,
and after inflating the view,
it returns the view.
Alright so next is our onBindViewHolder function
and that's called when the RecyclerView wants new data
to be displayed and is providing
an existing view to be re-eused.
So this is where we put our data
into the individual widgets.
In case it's not obvious,
a call to onCreateViewHolder will be followed
by a call to onBindViewHolder.
The RecyclerView first requests a new view,
then sends the view back so that we can put data into it.
So let's implement that.
So, I'm going to delete the TODO.
And we'll start with a log.
So I have log.d, parentheses,
TAG, "onBindViewHolder
:starts" logging message.
You're going to avoid some problems
with the smart cast again.
So, val cursor = cursor
now just to make it clear,
we're going to put that in a comment
for some regular code in the future.
Of course we talked about that previously.
Alright, first in length then we want
to check to see whether the cursor is null,
or it has a count of zero.
Now record if, parenthesis, cursor = null,
or cursor.,
or cursor.count.
I'm trying to use the word period instead of dot.
For dots very normal for me to use in Australia,
but it's not something that's used over-seas,
so I'm trying to use the word period
on a consistent basis,
so bear with me there,
so that everyone knows what I'm talking about.
= 0
Then we're just going to log a message.
Basically what we're doing,
is we're providing some instructions,
if this is the first entry.
So Log.d parentheses TAG,
then our message will be
onBindViewHolder:
providing instructions,
so again we know what's going on in the log.
Then we're gonna set the T line name field properly
to instructions.
So holder.tli_name.
setText parentheses,
and it's gonna be Instructions,
in double quotes.
Android Studio should've added the synthetic import of
kotlinx.android.synthetic.main.task_list_items.*
And the one I'm talking about is the one there on line ten.
So it's done that for us automatically.
What we've done there,
is enabled the use of synthetic imports
in our ViewHolder class by implementing
the LayoutContainer interface that we did last video,
and I think that's pretty cool.
Alright, with that said,
let's now finish the function,
so go back down to where we were.
Now onBindViewHolder.
We're testing for no records there.
What we need to do,
we'll set instructions,
we need to update the other fields as well.
So it's gonna be holder.tli_description
and we set that to an empty string.
.setText parenthesis, two double quotes,
you wanna hide the edit and delete properly,
so holder.tli
_edit
.visibility =
and we'll set that to View.GONE.
We'll do the same with delete.
So holder.tli_delete
.visibilty =
View.GONE.
Alright, so if the cursor was null,
there's no records.
We now need to add an else clause,
which is obviously gonna be taken up,
if that's not the case,
in other words, we have got some dollars.
Now the else will be wiping the code block.
First we wanna try and see
whether we can move to another position,
so we do that by typing if, parenthesis, not,
cursor.moveToPosition,
in parenthesis, position,
closing your right parentheses.
I'm not gonna code block,
and if we can't move to that position,
we're gonna throw an illegal state exception.
So throw IllegalStateException
and in parentheses,
we'll put a message to that effect,
Couldn't move cursor to position
$position.
Obviously we had an error trying to do that.
Alright, that's handled that scenario.
Now assuming that we got past that
and haven't thrown an error,
We're gonna create a task object from
the data that's currently in the cursor,
so we'll put a comment to that effect.
Create a Task object
from the data in the cursor.
Now we'll start by typing val task = Task parentheses.
Now you're gonna type cursor.getString,
now we've got parentheses there,
then I'll type cursor.getColumnIndex
and parenthesis again
and then TasksContract.Columns.
TASK_NAME,
first field, comma,
and then the next one will be cursor.getString.
Let's save a bit of time here
and let's just take a copy of these next two.
So, on line 53 it's
cursor.getString,
then cursor.getColumnIndex,
and this time it's gonna be basically
task description instead of task name,
and the last one is going to be task sort order.
So task sort order.
The last one isn't gonna have a comma,
'cause we're closing off the line,
so we're gonna have a third raw parentheses,
and of course the third one is
the string sort order is in Int.
So .getInt.
Alright, so that fixes up that error.
Alright so, now that we've done that,
We'll put another comment down here
just to remind us about the ID,
so, remember that the ID isn't set in the constructor.
We need to set that manually.
Task.id = cursor.
And we want to do getLong,
parentheses, and it's going to be
cursor.getColumnIndex,
in parantheses again,
it's going to be TasksContract.Columns.ID,
and closing in raw parentheses.
The last thing we need to do is,
use the holder object to update our field.
So, holder.tli_name.text.
Like I said, our fields,
but of course it's our text views we're updating here.
Set that = task.name.
That's the first one, then we got three more to do.
Tli_description we'll set that =
task.description.
Then for our edit and delete,
holder.tli_edit,
.visisbilty, we'll set that = View.VISIBLE.
Now we'll do the same for delete.
Holder.tli_delete.visibilty
= View.VISIBLE.
I think I've got an extra raw parentheses there,
which I will delete.
And I missed something in there,
it should have been .text,
so tli_description.text
= task_description, that fixes that.
Alright now, the description text,
this is for the instructions,
which we've got currently on line 42,
is quite long.
It provides basic instructions to the user
when the app first loads up.
Obviously we've got the name defined as instructions,
but the text is an empty string at the moment.
Now I'm sure you don't want
to see me typing in a load of text,
so what I'm going to do,
is paste in the holder. description line
with the text we want.
You can pause the video to catch up,
and what I'll also do,
is I'll put it in the resources section as a text file,
you can grab it from there and copy and paste
if you'd prefer to do that.
So let's do that.
I'm gonna change line 42
and paste in the description.
Okay.
Once again, this is all very similar
to what we did in the flicker browser app.
Instead of retrieving the data from a list,
we get data from a cursor and use it
to create a new task object.
If the cursor is null,
and we can see some of the code there,
or it contains no rows,
then we put some instructions in the description text view,
and the heading in the name.
We've seen \n used before to insert a long break in text.
We just formulate a text there for description
so that it looks better when it's displayed.
We also set the buttons to be invisible;
lines 47 and 48.
There's a view.invisible view value defined,
but I've used View.GONE instead.
It's very similar to invisible,
but the widget also takes up no space in the layout.
The other layout is a description text view widget
to fill the entire width of the display.
Once the code's running,
try changing those two settings to view.invisible
to see the difference that it makes.
As long as we have a valid cursor
that contains at least one row for code in the else block,
starting on line 50,
moves to a correct position in the cursor.
And obviously, that's assuming that we don't get
an error trying to move to that position.
After that, we're retrieving the values from the cursor
and using them to set the text in the TextView widgets.
We can also make the buttons visible again
down there towards the bottom on lines 64 and 65.
We're gonna add onClick list this to the buttons.
So what I'll do there for those two,
is we'll make a note to that effect, as a TODO.
So I'm gonna put a TODO there
to remind us to add an onClick.
It's always a good idea to that,
'cause of course you can see those TODOs
if you want to at a later stage.
Pretty handy, you can see that Android Studio
is highlighting them in the blue.
If you want to see your TODOs,
I think I've shown you this before,
click on TODO and you get a list of all the TODOs.
And there's one there from us that we've used previously,
and also this one here,
CursorRecyclerViewAdapter that we're working on now.
Very handy to have that capability.
Alright, I'll close that now.
Basically, a useful way to keep track of code
that you want to come back to,
and it can help to make sure that you don't forget.
We're also getting a warning now,
on the right hand margin.
It's three, in fact, but the first one,
we come back here and look at the first one.
So ignore this first one about removing
the empty class body,
because I talked about that,
but this one here
is telling us that currently CursorRecyclerViewAdapter,
the class itself isn't being used at the moment.
More to the point, is these other ones down here,
we need to deal with these,
which is a warning about hard-coded text,
and obviously that's because we're using
a string literal in the setText function call.
We can get Android Studio to help
in creating a string resources for us,
which is slightly easier than typing the XML into
the string's XML file,
which is of course Res/values file.
I'll do that in the next video,
because it's a bit fiddlier
than you might expect.
See you in the next video.