Showing posts with label cursor. Show all posts
Showing posts with label cursor. Show all posts

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.



23 December 2013

Android show contact email addresses

So this is something in Android I thought would be really simple but quite frankly, wasn't. I want to get a list of all email addresses in my phone and display them in a list. If one is to use ContactsContract.CommonDataKinds.Email.CONTENT_URI without filter you basically get every email address you've ever emailed. Not ideal. I am (in this case) looking for only our saved Google contacts. We can do this query in the UI thread, which works fine but if you have several hundred contacts that'll kill the app. So we're going to use LoaderManager. First we setup out fragment:

public class FragmentMain extends Fragment implements LoaderManager.LoaderCallbacks{

 ArrayList mFriends = new ArrayList();

...
}

Now we need to setup our createLoader method to get the data and onLoadFinished to put the data into the UI. The selection here is very important as it limits the query to the visible contacts: String my_selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1'";.
Next step is very simple, in the onLoadFinished we loop through the available data, adding each email address to an ArrayList.


 @Override
 public Loader onCreateLoader(int arg0, Bundle arg1) {
  //Sort by email address no case
  final String my_sort_order = ContactsContract.CommonDataKinds.Email.DATA + " COLLATE NOCASE ASC";
  String my_selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1'";
  String[] eproj = new String[]{
   ContactsContract.Contacts._ID,
   ContactsContract.CommonDataKinds.Email.DATA};
  Uri uri = android.provider.ContactsContract.CommonDataKinds.Email.CONTENT_URI;
  //Query all emails in contacts
  return new CursorLoader(getActivity(), uri, eproj, my_selection, null, my_sort_order);
 }

 @Override
 public void onLoadFinished(Loader arg0, Cursor data) {
  
  if (data != null && data.moveToFirst()) {
   //Loop through cursor
   while (!data.isAfterLast()) {
    //get Email address
    String myemail = data.getString(data.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
    if(!mFriends.contains(myemail)){
     //Add email to arrayList
     mFriends.add(myemail);
    }
    
    data.moveToNext();
   }
  }
  //load friends into ui
  loadFriends();
 }

 @Override
 public void onLoaderReset(Loader arg0) {
  //Not Used
 }

Last but not least we load the data into our listview. Now you can of course, should you desire, create a custom Adapter and implement your own item view, but that's a blog post for another day.


 private void loadFriends(){
  //load arraylist into adapter.
  ArrayAdapter adapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, mFriends);
  mFriendsList.setAdapter(adapter);
 }

Oh and one more thing, don't forget your permissions :)
<uses-permission android:name="android.permission.READ_CONTACTS" />

08 August 2013

Android, extending listView functinality


Android listViews are pretty useful and pretty simple. You have to get your head around some sort of adapter, but that's no huge issue.

However I've previously never bothered to find out how to get more than one column on a listView. Luckily this is much easier than I first thought. My example uses contentProvidors to setup the cursor, but I think this should work with DB stuff as well. Given a bit of hocus pocus.

I'm not going to go into the contentProvidor stuff here as it's a big subject and developer.android do a pretty good job:
http://developer.android.com/guide/topics/providers/content-provider-basics.html

First setup your activity_main.xml layout file:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
 
 <ListView
  android:id="@+id/listView1"
  android:layout_width="wrap_content"
  android:layout_height="350dp"
  android:layout_alignParentBottom="true"
  android:layout_centerHorizontal="true" >
 </ListView>

</RelativeLayout>


Next add a new xml file to the res/layout folder (mine is named contacts_single.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="horizontal" >

 <TextView
  android:id="@+id/name_entry"
  android:layout_width="100dp"
  android:layout_height="wrap_content" />
 
 <TextView
  android:id="@+id/number_entry"
  android:layout_width="100dp"
  android:layout_height="wrap_content" />

</LinearLayout>


Now move to the MainActivity or wherever your display function will be and you'll need something like this:

private void outputCursor(){
 // Defines a list of columns which we have defined in our cursor, and will be output to listView
 String[] mWordListColumns =
 {
  ContactsContract.Contacts._ID,
  ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
  
 };

 // Defines a list of View IDs. These are IDs of textViews defined in a serperate layout file
 int[] mWordListItems = { R.id.name_entry, R.id.number_entry};

 // Creates a new SimpleCursorAdapter
 SimpleCursorAdapter mCursorAdapter = new SimpleCursorAdapter(
  getApplicationContext(),   // The application's Context object
  R.layout.contacts_single,   // A layout in XML for one row in the ListView
  mCursor,     // The result from the query
  mWordListColumns,    // A string array of column names in the cursor
  mWordListItems,     // An integer array of view IDs in the row layout
  0      // Flags (usually none are needed)
 );

 // Sets the adapter for the ListView
 ListView myListView = (ListView) findViewById(R.id.listView1);
 myListView.setAdapter(mCursorAdapter);
}

That should be all you need. Note the mWordListItems is passed into the adapter and the R.layout.contacts_single reference, telling the adapter to use these custom elements to display its contents. Then we get the ListView from the original activity and set the adapter. Simples!