Custom CursorAdapters

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:

Custom CursorAdapters

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.214.155