04 November 2014

Android CursorAdapter


So the other day some stack overflow numpty yelled at me that I should use a cursor adapter instead of an array adapter. Well, just a point of habbit I thought. However I'd not extensively used a cursor adapter before, so I thought I'd give it a shot before I dismissed his suggestion.

Anyway I decided to come up with a simple example as there aren't that many examples of straight cursorAdapters out there and simpleCursorAdapters just don't cut it. The basic idea of a cursor adapter is to directly link to database adapter, rather than say an array adapter, which displays an array of any type of data.

So first grab you data, obviously this can be whatever you want. I copied a Google contacts example and simplified it.

This is inside a Fragment:

    @SuppressLint("InlinedApi") 
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        
        @SuppressLint("InlinedApi")
        final String SORT_ORDER = Utils.hasHoneycomb() ? Contacts.SORT_KEY_PRIMARY : Contacts.DISPLAY_NAME;
        
        @SuppressLint("InlinedApi")
        final String[] PROJECTION = {
                Contacts._ID,
                Contacts.LOOKUP_KEY,
                Utils.hasHoneycomb() ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME,
                Utils.hasHoneycomb() ? Contacts.PHOTO_THUMBNAIL_URI : Contacts._ID
        };
        
        final String SELECTION =
                (Utils.hasHoneycomb() ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME) +
                "<>''" + " AND " + Contacts.IN_VISIBLE_GROUP + "=1";
        
        if(id == QUERY_ID){
            return new CursorLoader(getActivity(),
                    Contacts.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    null,
                    SORT_ORDER);
        }
        
        return null;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        if (loader.getId() == QUERY_ID) {
            mAdapter.swapCursor(data);
            mAdapter.notifyDataSetChanged();
        }
    }


Then we need our adapter:

public class AdapterNewContacts extends CursorAdapter{
    
    private LayoutInflater mInflater;
    
    public AdapterNewContacts(Context context) {
        super(context, null, 0);

        mInflater = LayoutInflater.from(context);
    }
    
    @SuppressLint("InlinedApi") 
    public void bindView(View view, Context context, Cursor cursor) {
        
        final ViewHolder holder = (ViewHolder) view.getTag();
        
        holder.textName.setText(
            cursor.getString(cursor.getColumnIndex((Utils.hasHoneycomb() ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME)))
        );
    }

    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View convertView = mInflater.inflate(R.layout.contacts_list_item, parent, false);
        
        final ViewHolder holder = new ViewHolder();
        holder.textName = (TextView) convertView.findViewById(R.id.contacts_list_item_name);
        
        convertView.setTag(holder);
        return convertView;
    }
    
    private class ViewHolder {
        TextView textName;
    }
}



That's it! Notice the difference between this and our normal ArrayAdapter, there's no getView() and we actually have one method for creating the view and one for re-aquiring the view. This is different from a getView where we have to create and get views in one method.

So pretty simple. I still don't agree with whoever suggested this though, it's too restrictive. In an ArrayAdapter I can create my own dataset and update it however I want. With this example I'm limited to only the one cursor.

Oh well, it was an interesting experiment.



No comments: