| page.title=Displaying the Quick Contact Badge |
| |
| trainingnavtop=true |
| @jd:body |
| |
| |
| <div id="tb-wrapper"> |
| <div id="tb"> |
| |
| <!-- table of contents --> |
| <h2>This lesson teaches you to</h2> |
| <ol> |
| <li> |
| <a href="#AddView">Add a QuickContactBadge View</a> |
| </li> |
| <li> |
| <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a> |
| </li> |
| <li> |
| <a href="#ListView"> |
| Add a QuickContactBadge to a ListView |
| </a> |
| </li> |
| </ol> |
| |
| <!-- other docs (NOT javadocs) --> |
| <h2>You should also read</h2> |
| <ul> |
| <li> |
| <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> |
| Content Provider Basics |
| </a> |
| </li> |
| <li> |
| <a href="{@docRoot}guide/topics/providers/contacts-provider.html"> |
| Contacts Provider |
| </a> |
| </li> |
| </ul> |
| |
| <h2>Try it out</h2> |
| |
| <div class="download-box"> |
| <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button"> |
| Download the sample |
| </a> |
| <p class="filename">ContactsList.zip</p> |
| </div> |
| |
| </div> |
| </div> |
| <p> |
| This lesson shows you how to add a {@link android.widget.QuickContactBadge} to your UI |
| and how to bind data to it. A {@link android.widget.QuickContactBadge} is a widget that |
| initially appears as a thumbnail image. Although you can use any {@link android.graphics.Bitmap} |
| for the thumbnail image, you usually use a {@link android.graphics.Bitmap} decoded from the |
| contact's photo thumbnail image. |
| </p> |
| <p> |
| The small image acts as a control; when users click on the image, the |
| {@link android.widget.QuickContactBadge} expands into a dialog containing the following: |
| </p> |
| <dl> |
| <dt>A large image</dt> |
| <dd> |
| The large image associated with the contact, or no image is available, a placeholder |
| graphic. |
| </dd> |
| <dt> |
| App icons |
| </dt> |
| <dd> |
| An app icon for each piece of detail data that can be handled by a built-in app. For |
| example, if the contact's details include one or more email addresses, an email icon |
| appears. When users click the icon, all of the contact's email addresses appear. When users |
| click one of the addresses, the email app displays a screen for composing a message to the |
| selected email address. |
| </dd> |
| </dl> |
| <p> |
| The {@link android.widget.QuickContactBadge} view provides instant access to a contact's |
| details, as well as a fast way of communicating with the contact. Users don't have to look up |
| a contact, find and copy information, and then paste it into the appropriate app. Instead, they |
| can click on the {@link android.widget.QuickContactBadge}, choose the communication method they |
| want to use, and send the information for that method directly to the appropriate app. |
| </p> |
| <h2 id="AddView">Add a QuickContactBadge View</h2> |
| <p> |
| To add a {@link android.widget.QuickContactBadge}, insert a |
| <code><QuickContactBadge></code> element in your layout. For example: |
| </p> |
| <pre> |
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| android:layout_width="match_parent" |
| android:layout_height="match_parent"> |
| ... |
| <QuickContactBadge |
| android:id=@+id/quickbadge |
| android:layout_height="wrap_content" |
| android:layout_width="wrap_content" |
| android:scaleType="centerCrop"/> |
| ... |
| </RelativeLayout> |
| </pre> |
| <h2 id="">Retrieve provider data</h2> |
| <p> |
| To display a contact in the {@link android.widget.QuickContactBadge}, you need a content URI |
| for the contact and a {@link android.graphics.Bitmap} for the small image. You generate |
| both the content URI and the {@link android.graphics.Bitmap} from columns retrieved from the |
| Contacts Provider. Specify these columns as part of the projection you use to load data into |
| your {@link android.database.Cursor}. |
| </p> |
| <p> |
| For Android 3.0 (API level 11) and later, include the following columns in your projection:</p> |
| <ul> |
| <li>{@link android.provider.BaseColumns#_ID Contacts._ID}</li> |
| <li>{@link android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY Contacts.LOOKUP_KEY}</li> |
| <li> |
| {@link android.provider.ContactsContract.ContactsColumns#PHOTO_THUMBNAIL_URI |
| Contacts.PHOTO_THUMBNAIL_URI} |
| </li> |
| </ul> |
| <p> |
| For Android 2.3.3 (API level 10) and earlier, use the following columns: |
| </p> |
| <ul> |
| <li>{@link android.provider.BaseColumns#_ID Contacts._ID}</li> |
| <li>{@link android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY Contacts.LOOKUP_KEY}</li> |
| </ul> |
| <p> |
| The remainder of this lesson assumes that you've already loaded a |
| {@link android.database.Cursor} that contains these columns as well as others you may have |
| chosen. To learn how to retrieve this columns in a {@link android.database.Cursor}, read the |
| lesson <a href="retrieve-names.html">Retrieving a List of Contacts</a>. |
| </p> |
| <h2 id="SetURIThumbnail">Set the Contact URI and Thumbnail</h2> |
| <p> |
| Once you have the necessary columns, you can bind data to the |
| {@link android.widget.QuickContactBadge}. |
| </p> |
| <h3>Set the Contact URI</h3> |
| <p> |
| To set the content URI for the contact, call |
| {@link android.provider.ContactsContract.Contacts#getLookupUri getLookupUri(id,lookupKey)} to |
| get a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, then |
| call {@link android.widget.QuickContactBadge#assignContactUri assignContactUri()} to set the |
| contact. For example: |
| </p> |
| <pre> |
| // The Cursor that contains contact rows |
| Cursor mCursor; |
| // The index of the _ID column in the Cursor |
| int mIdColumn; |
| // The index of the LOOKUP_KEY column in the Cursor |
| int mLookupKeyColumn; |
| // A content URI for the desired contact |
| Uri mContactUri; |
| // A handle to the QuickContactBadge view |
| QuickContactBadge mBadge; |
| ... |
| mBadge = (QuickContactBadge) findViewById(R.id.quickbadge); |
| /* |
| * Insert code here to move to the desired cursor row |
| */ |
| // Gets the _ID column index |
| mIdColumn = mCursor.getColumnIndex(Contacts._ID); |
| // Gets the LOOKUP_KEY index |
| mLookupKeyColumn = mCursor.getColumnIndex(Contacts.LOOKUP_KEY); |
| // Gets a content URI for the contact |
| mContactUri = |
| Contacts.getLookupUri( |
| mCursor.getLong(mIdColumn), |
| mCursor.getString(mLookupKeyColumn) |
| ); |
| mBadge.assignContactUri(mContactUri); |
| </pre> |
| <p> |
| When users click the {@link android.widget.QuickContactBadge} icon, the contact's |
| details automatically appear in the dialog. |
| </p> |
| <h3>Set the photo thumbnail</h3> |
| <p> |
| Setting the contact URI for the {@link android.widget.QuickContactBadge} does not automatically |
| load the contact's thumbnail photo. To load the photo, get a URI for the photo from the |
| contact's {@link android.database.Cursor} row, use it to open the file containing the compressed |
| thumbnail photo, and read the file into a {@link android.graphics.Bitmap}. |
| </p> |
| <p class="note"> |
| <strong>Note:</strong> The |
| {@link android.provider.ContactsContract.ContactsColumns#PHOTO_THUMBNAIL_URI} column isn't available |
| in platform versions prior to 3.0. For those versions, you must retrieve the URI |
| from the {@link android.provider.ContactsContract.Contacts.Photo Contacts.Photo} subtable. |
| </p> |
| <p> |
| First, set up variables for accessing the {@link android.database.Cursor} containing the |
| {@link android.provider.BaseColumns#_ID Contacts._ID} and |
| {@link android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY Contacts.LOOKUP_KEY} columns, as |
| described previously: |
| </p> |
| <pre> |
| // The column in which to find the thumbnail ID |
| int mThumbnailColumn; |
| /* |
| * The thumbnail URI, expressed as a String. |
| * Contacts Provider stores URIs as String values. |
| */ |
| String mThumbnailUri; |
| ... |
| /* |
| * Gets the photo thumbnail column index if |
| * platform version >= Honeycomb |
| */ |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { |
| mThumbnailColumn = |
| mCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI); |
| // Otherwise, sets the thumbnail column to the _ID column |
| } else { |
| mThumbnailColumn = mIdColumn; |
| } |
| /* |
| * Assuming the current Cursor position is the contact you want, |
| * gets the thumbnail ID |
| */ |
| mThumbnailUri = mCursor.getString(mThumbnailColumn); |
| ... |
| </pre> |
| <p> |
| Define a method that takes photo-related data for the contact and dimensions for the |
| destination view, and returns the properly-sized thumbnail in a |
| {@link android.graphics.Bitmap}. Start by constructing a URI that points to the |
| thumbnail: |
| <p> |
| <pre> |
| /** |
| * Load a contact photo thumbnail and return it as a Bitmap, |
| * resizing the image to the provided image dimensions as needed. |
| * @param photoData photo ID Prior to Honeycomb, the contact's _ID value. |
| * For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI. |
| * @return A thumbnail Bitmap, sized to the provided width and height. |
| * Returns null if the thumbnail is not found. |
| */ |
| private Bitmap loadContactPhotoThumbnail(String photoData) { |
| // Creates an asset file descriptor for the thumbnail file. |
| AssetFileDescriptor afd = null; |
| // try-catch block for file not found |
| try { |
| // Creates a holder for the URI. |
| Uri thumbUri; |
| // If Android 3.0 or later |
| if (Build.VERSION.SDK_INT |
| >= |
| Build.VERSION_CODES.HONEYCOMB) { |
| // Sets the URI from the incoming PHOTO_THUMBNAIL_URI |
| thumbUri = Uri.parse(photoData); |
| } else { |
| // Prior to Android 3.0, constructs a photo Uri using _ID |
| /* |
| * Creates a contact URI from the Contacts content URI |
| * incoming photoData (_ID) |
| */ |
| final Uri contactUri = Uri.withAppendedPath( |
| Contacts.CONTENT_URI, photoData); |
| /* |
| * Creates a photo URI by appending the content URI of |
| * Contacts.Photo. |
| */ |
| thumbUri = |
| Uri.withAppendedPath( |
| contactUri, Photo.CONTENT_DIRECTORY); |
| } |
| |
| /* |
| * Retrieves an AssetFileDescriptor object for the thumbnail |
| * URI |
| * using ContentResolver.openAssetFileDescriptor |
| */ |
| afd = getActivity().getContentResolver(). |
| openAssetFileDescriptor(thumbUri, "r"); |
| /* |
| * Gets a file descriptor from the asset file descriptor. |
| * This object can be used across processes. |
| */ |
| FileDescriptor fileDescriptor = afd.getFileDescriptor(); |
| // Decode the photo file and return the result as a Bitmap |
| // If the file descriptor is valid |
| if (fileDescriptor != null) { |
| // Decodes the bitmap |
| return BitmapFactory.decodeFileDescriptor( |
| fileDescriptor, null, null); |
| } |
| // If the file isn't found |
| } catch (FileNotFoundException e) { |
| /* |
| * Handle file not found errors |
| */ |
| // In all cases, close the asset file descriptor |
| } finally { |
| if (afd != null) { |
| try { |
| afd.close(); |
| } catch (IOException e) {} |
| } |
| } |
| return null; |
| } |
| </pre> |
| <p> |
| Call the <code>loadContactPhotoThumbnail()</code> method in your code to get the |
| thumbnail {@link android.graphics.Bitmap}, and use the result to set the photo thumbnail in |
| your {@link android.widget.QuickContactBadge}: |
| </p> |
| <pre> |
| ... |
| /* |
| * Decodes the thumbnail file to a Bitmap. |
| */ |
| Bitmap mThumbnail = |
| loadContactPhotoThumbnail(mThumbnailUri); |
| /* |
| * Sets the image in the QuickContactBadge |
| * QuickContactBadge inherits from ImageView, so |
| */ |
| mBadge.setImageBitmap(mThumbnail); |
| </pre> |
| <h2 id="ListView">Add a QuickContactBadge to a ListView</h2> |
| <p> |
| A {@link android.widget.QuickContactBadge} is a useful addition to a |
| {@link android.widget.ListView} that displays a list of contacts. Use the |
| {@link android.widget.QuickContactBadge} to display a thumbnail photo for each contact; when |
| users click the thumbnail, the {@link android.widget.QuickContactBadge} dialog appears. |
| </p> |
| <h3>Add the QuickContactBadge element</h3> |
| <p> |
| To start, add a {@link android.widget.QuickContactBadge} view element to your item layout |
| For example, if you want to display a {@link android.widget.QuickContactBadge} and a name for |
| each contact you retrieve, put the following XML into a layout file: |
| </p> |
| <pre> |
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| android:layout_width="match_parent" |
| android:layout_height="wrap_content"> |
| <QuickContactBadge |
| android:id="@+id/quickcontact" |
| android:layout_height="wrap_content" |
| android:layout_width="wrap_content" |
| android:scaleType="centerCrop"/> |
| <TextView android:id="@+id/displayname" |
| android:layout_width="match_parent" |
| android:layout_height="wrap_content" |
| android:layout_toRightOf="@+id/quickcontact" |
| android:gravity="center_vertical" |
| android:layout_alignParentRight="true" |
| android:layout_alignParentTop="true"/> |
| </RelativeLayout> |
| </pre> |
| <p> |
| In the following sections, this file is referred to as <code>contact_item_layout.xml</code>. |
| </p> |
| <h3>Set up a custom CursorAdapter</h3> |
| <p> |
| To bind a {@link android.support.v4.widget.CursorAdapter} to a {@link android.widget.ListView} |
| containing a {@link android.widget.QuickContactBadge}, define a custom adapter that |
| extends {@link android.support.v4.widget.CursorAdapter}. This approach allows you to process the |
| data in the {@link android.database.Cursor} before you bind it to the |
| {@link android.widget.QuickContactBadge}. This approach also allows you to bind multiple |
| {@link android.database.Cursor} columns to the {@link android.widget.QuickContactBadge}. Neither |
| of these operations is possible in a regular {@link android.support.v4.widget.CursorAdapter}. |
| </p> |
| <p> |
| The subclass of {@link android.support.v4.widget.CursorAdapter} that you define must |
| override the following methods: |
| </p> |
| <dl> |
| <dt>{@link android.support.v4.widget.CursorAdapter#newView CursorAdapter.newView()}</dt> |
| <dd> |
| Inflates a new {@link android.view.View} object to hold the item layout. In the override |
| of this method, store handles to the child {@link android.view.View} objects of the layout, |
| including the child {@link android.widget.QuickContactBadge}. By taking this approach, you |
| avoid having to get handles to the child {@link android.view.View} objects each time you |
| inflate a new layout. |
| <p> |
| You must override this method so you can get handles to the individual child |
| {@link android.view.View} objects. This technique allows you to control their binding in |
| {@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}. |
| </p> |
| </dd> |
| <dt>{@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}</dt> |
| <dd> |
| Moves data from the current {@link android.database.Cursor} row to the child |
| {@link android.view.View} objects of the item layout. You must override this method so |
| you can bind both the contact's URI and thumbnail to the |
| {@link android.widget.QuickContactBadge}. The default implementation only allows a 1-to-1 |
| mapping between a column and a {@link android.view.View} |
| </dd> |
| </dl> |
| <p> |
| The following code snippet contains an example of a custom subclass of |
| {@link android.support.v4.widget.CursorAdapter}: |
| </p> |
| <h3>Define the custom list adapter</h3> |
| <p> |
| Define the subclass of {@link android.support.v4.widget.CursorAdapter} including its |
| constructor, and override |
| {@link android.support.v4.widget.CursorAdapter#newView newView()} and |
| {@link android.support.v4.widget.CursorAdapter#bindView bindView()}: |
| </p> |
| <pre> |
| /** |
| * |
| * |
| */ |
| private class ContactsAdapter extends CursorAdapter { |
| private LayoutInflater mInflater; |
| ... |
| public ContactsAdapter(Context context) { |
| super(context, null, 0); |
| |
| /* |
| * Gets an inflater that can instantiate |
| * the ListView layout from the file. |
| */ |
| mInflater = LayoutInflater.from(context); |
| ... |
| } |
| ... |
| /** |
| * Defines a class that hold resource IDs of each item layout |
| * row to prevent having to look them up each time data is |
| * bound to a row. |
| */ |
| private class ViewHolder { |
| TextView displayname; |
| QuickContactBadge quickcontact; |
| } |
| .. |
| @Override |
| public View newView( |
| Context context, |
| Cursor cursor, |
| ViewGroup viewGroup) { |
| /* Inflates the item layout. Stores resource IDs in a |
| * in a ViewHolder class to prevent having to look |
| * them up each time bindView() is called. |
| */ |
| final View itemView = |
| mInflater.inflate( |
| R.layout.contact_list_layout, |
| viewGroup, |
| false |
| ); |
| final ViewHolder holder = new ViewHolder(); |
| holder.displayname = |
| (TextView) view.findViewById(R.id.displayname); |
| holder.quickcontact = |
| (QuickContactBadge) |
| view.findViewById(R.id.quickcontact); |
| view.setTag(holder); |
| return view; |
| } |
| ... |
| @Override |
| public void bindView( |
| View view, |
| Context context, |
| Cursor cursor) { |
| final ViewHolder holder = (ViewHolder) view.getTag(); |
| final String photoData = |
| cursor.getString(mPhotoDataIndex); |
| final String displayName = |
| cursor.getString(mDisplayNameIndex); |
| ... |
| // Sets the display name in the layout |
| holder.displayname = cursor.getString(mDisplayNameIndex); |
| ... |
| /* |
| * Generates a contact URI for the QuickContactBadge. |
| */ |
| final Uri contactUri = Contacts.getLookupUri( |
| cursor.getLong(mIdIndex), |
| cursor.getString(mLookupKeyIndex)); |
| holder.quickcontact.assignContactUri(contactUri); |
| String photoData = cursor.getString(mPhotoDataIndex); |
| /* |
| * Decodes the thumbnail file to a Bitmap. |
| * The method loadContactPhotoThumbnail() is defined |
| * in the section "Set the Contact URI and Thumbnail" |
| */ |
| Bitmap thumbnailBitmap = |
| loadContactPhotoThumbnail(photoData); |
| /* |
| * Sets the image in the QuickContactBadge |
| * QuickContactBadge inherits from ImageView |
| */ |
| holder.quickcontact.setImageBitmap(thumbnailBitmap); |
| } |
| </pre> |
| |
| <h3>Set up variables</h3> |
| <p> |
| In your code, set up variables, including a {@link android.database.Cursor} projection that |
| includes the necessary columns. |
| </p> |
| <p class="note"> |
| <strong>Note:</strong> The following code snippets use the method |
| <code>loadContactPhotoThumbnail()</code>, which is defined in the section |
| <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a> |
| </p> |
| <p> |
| For example: |
| </p> |
| <pre> |
| public class ContactsFragment extends Fragment implements |
| LoaderManager.LoaderCallbacks<Cursor> { |
| ... |
| // Defines a ListView |
| private ListView mListView; |
| // Defines a ContactsAdapter |
| private ContactsAdapter mAdapter; |
| ... |
| // Defines a Cursor to contain the retrieved data |
| private Cursor mCursor; |
| /* |
| * Defines a projection based on platform version. This ensures |
| * that you retrieve the correct columns. |
| */ |
| private static final String[] PROJECTION = |
| { |
| Contacts._ID, |
| Contacts.LOOKUP_KEY, |
| (Build.VERSION.SDK_INT >= |
| Build.VERSION_CODES.HONEYCOMB) ? |
| Contacts.DISPLAY_NAME_PRIMARY : |
| Contacts.DISPLAY_NAME |
| (Build.VERSION.SDK_INT >= |
| Build.VERSION_CODES.HONEYCOMB) ? |
| Contacts.PHOTO_THUMBNAIL_ID : |
| /* |
| * Although it's not necessary to include the |
| * column twice, this keeps the number of |
| * columns the same regardless of version |
| */ |
| Contacts_ID |
| ... |
| }; |
| /* |
| * As a shortcut, defines constants for the |
| * column indexes in the Cursor. The index is |
| * 0-based and always matches the column order |
| * in the projection. |
| */ |
| // Column index of the _ID column |
| private int mIdIndex = 0; |
| // Column index of the LOOKUP_KEY column |
| private int mLookupKeyIndex = 1; |
| // Column index of the display name column |
| private int mDisplayNameIndex = 3; |
| /* |
| * Column index of the photo data column. |
| * It's PHOTO_THUMBNAIL_URI for Honeycomb and later, |
| * and _ID for previous versions. |
| */ |
| private int mPhotoDataIndex = |
| Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? |
| 3 : |
| 0; |
| ... |
| </pre> |
| <h3>Set up the ListView</h3> |
| <p> |
| In {@link android.support.v4.app.Fragment#onCreate Fragment.onCreate()}, instantiate the custom |
| cursor adapter and get a handle to the {@link android.widget.ListView}: |
| </p> |
| <pre> |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| ... |
| /* |
| * Instantiates the subclass of |
| * CursorAdapter |
| */ |
| ContactsAdapter mContactsAdapter = |
| new ContactsAdapter(getActivity()); |
| /* |
| * Gets a handle to the ListView in the file |
| * contact_list_layout.xml |
| */ |
| mListView = (ListView) findViewById(R.layout.contact_list_layout); |
| ... |
| } |
| ... |
| </pre> |
| <p> |
| In {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, bind the |
| <code>ContactsAdapter</code> to the {@link android.widget.ListView}: |
| </p> |
| <pre> |
| @Override |
| public void onActivityCreated(Bundle savedInstanceState) { |
| ... |
| // Sets up the adapter for the ListView |
| mListView.setAdapter(mAdapter); |
| ... |
| } |
| ... |
| </pre> |
| <p> |
| When you get back a {@link android.database.Cursor} containing the contacts data, usually in |
| {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}, |
| call {@link android.support.v4.widget.CursorAdapter#swapCursor swapCursor()} to move the |
| {@link android.database.Cursor} data to the {@link android.widget.ListView}. This displays the |
| {@link android.widget.QuickContactBadge} for each entry in the list of contacts: |
| </p> |
| <pre> |
| public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { |
| // When the loader has completed, swap the cursor into the adapter. |
| mContactsAdapter.swapCursor(cursor); |
| } |
| </pre> |
| <p> |
| When you bind a {@link android.database.Cursor} to a |
| {@link android.widget.ListView} with a {@link android.support.v4.widget.CursorAdapter} |
| (or subclass), and you use a {@link android.support.v4.content.CursorLoader} to load the |
| {@link android.database.Cursor}, always clear references to the {@link android.database.Cursor} |
| in your implementation of |
| {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}. |
| For example: |
| </p> |
| <pre> |
| @Override |
| public void onLoaderReset(Loader<Cursor> loader) { |
| // Removes remaining reference to the previous Cursor |
| mContactsAdapter.swapCursor(null); |
| } |
| </pre> |