In this section, we will expand upon the SimpleCursorAdapter
and try to write our own CursorAdapter
class, which will give us greater flexibility in terms of how the underlying data is to be displayed. The goal of our custom class will be simple – instead of having the phone number types being displayed as integers, let's find a way to display them as readable Strings.
Upon extending the SimpleCursorAdapter
class, we'll need to override and implement the newView()
method, and most importantly the bindView()
method. Optionally, we can also customize our constructor, which depending on your implementation can be useful for caching and performance-enhancing reasons (we'll see an example of this later on).
Conceptually, what's happening here is that each time a new row is actually displayed on the Android device's screen, the newView()
method gets called. This means that as the user scrolls through the Activity's list and new rows appear on the device's screen (for the first time), this newView()
method will get called. And so, the functionality of this newView()
should be kept relatively straightforward. In my implementation, this means that given the context, I make a request for the associated LayoutInflater
class and use it to inflate the new row's layout (as defined in list_entry.xml
).
The meat of the logic then occurs in the
bindView()
method. Once the newView()
method is called and the actual layout of the row is initialized, the next method that gets called is the bindView()
method. This method takes as parameters the new View object that was previously instantiated, as well as the Cursor
that belongs to this adapter class. It's important to note that the Cursor
that's passed in has already been moved to the correct index. In other words, the adapter is smart enough to pass you a Cursor
that is pointing to the row of data corresponding to the row of your layout that you're creating! Now of course, it's hard to see and understand these methods without actually seeing the code side by side and so, before I go any further, let's take a quick look:
public class CustomContactsAdapter extends SimpleCursorAdapter { private int layout; public CustomContactsAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { super(context, layout, c, from, to); this.layout = layout; } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { final LayoutInflater inflater = LayoutInflater.from(context); View v = inflater.inflate(layout, parent, false); return v; } @Override public void bindView(View v, Context context, Cursor c) { int nameCol = c.getColumnIndex(Phone.DISPLAY_NAME); int numCol = c.getColumnIndex(Phone.NUMBER); int typeCol = c.getColumnIndex(Phone.TYPE); String name = c.getString(nameCol); String number = c.getString(numCol); int type = c.getInt(typeCol); String numType = ""; switch (type) { case Phone.TYPE_HOME: numType = "HOME"; break; case Phone.TYPE_MOBILE: numType = "MOBILE"; break; case Phone.TYPE_WORK: numType = "WORK"; break; default: numType = "MOBILE"; break; } // FIND THE VIEW AND SET THE NAME TextView name_text = (TextView) v.findViewById(R.id.name_entry); name_text.setText(name); TextView number_text = (TextView) v.findViewById(R.id.number_entry); number_text.setText(number); TextView type_text = (TextView) v.findViewById (R.id.number_type_entry); type_text.setText(numType); } }
Again, you'll notice that the newView()
method's implementation is pretty straightforward. You'll also notice that the Context being passed in is the same Context for each new row that is added – and so each time this method gets called, I'm actually requesting the same
LayoutInflater
object. Though it didn't make a noticeable difference in this case, little nuances like this (that is, not requesting the same resource continuously) are small ways in which you can optimize the performance of your lists. Here, by instantiating the LayoutInflater
a single time in the constructor and reusing it each time, we can potentially save hundreds of unnecessary requests. Though this may seem like a very minor optimization, keep in mind that when it comes to lists, especially on mobile devices, users expect them to be extremely snappy and responsive. A list that lags is often a huge nuisance to users over time, and is frequently indicative of a poorly written application.
Now for the bindView()
method. Again, the flow is that first newView()
gets called and a new row is instantiated, and then bindView()
gets called with this new row's layout view passed in. Here we have also passed a Cursor
object, but it's important to note that the Cursor
is actually pointing to the next row of data. In other words, the Cursor
is not pointing to the first row of the queried subtable but instead is pointing to a single row and is being incremented accordingly behind the scenes. This is what I mean by the CursorAdapter
class being a nice class to use because of how it handles the underlying Cursor
for you as the list scrolls up and down.
As for the logic in our binding – it's pretty simple. Given the Cursor
, we ask for the corresponding fields and their respective values, and since we're also passed the View object of that row, we just need to set the correct String value for each TextView
. However, notice that here we have the flexibility to insert additional logic which allows us to handle the fact that the phone number's type is returned as an integer. So, naturally we include the switch statement here, and instead of setting the integer into the type_text TextView
, we set the readable String value there!
Now, even though this is a pretty simple example, the goal of this exercise is to see how by extending the
SimpleCursorAdapter
class and implementing our own CursorAdapter
, we can override the bindView()
method and use the passed in View and Cursor
objects to customize our row's display in any way that we want!
As for how you actually use your custom CursorAdapter
in the previous SimpleCursorAdapter
example, simply swap out the following line:
SimpleCursorAdapter cAdapter = new SimpleCursorAdapter(this, R.layout.list_entry, c, columns, to);
with the line:
CustomContactsAdapter cAdapter = new CustomContactsAdapter(this, R.layout.list_entry, c, columns, to);
And how does this all look in the end? Let's take a quick look:
Here we see that in each row, instead of simply showing the integer type of the phone number, we can see the actual readable String type as desired! Much nicer now.
3.15.214.155