• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Displaying the Quick Contact Badge
2
3trainingnavtop=true
4@jd:body
5
6
7<div id="tb-wrapper">
8<div id="tb">
9
10<!-- table of contents -->
11<h2>This lesson teaches you to</h2>
12<ol>
13    <li>
14        <a href="#AddView">Add a QuickContactBadge View</a>
15    </li>
16    <li>
17        <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
18    </li>
19    <li>
20        <a href="#ListView">
21            Add a QuickContactBadge to a ListView
22        </a>
23    </li>
24</ol>
25
26<!-- other docs (NOT javadocs) -->
27<h2>You should also read</h2>
28<ul>
29    <li>
30        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
31        Content Provider Basics
32        </a>
33    </li>
34    <li>
35        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
36        Contacts Provider
37        </a>
38    </li>
39</ul>
40
41<h2>Try it out</h2>
42
43<div class="download-box">
44    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
45    Download the sample
46    </a>
47 <p class="filename">ContactsList.zip</p>
48</div>
49
50</div>
51</div>
52<p>
53    This lesson shows you how to add a {@link android.widget.QuickContactBadge} to your UI
54    and how to bind data to it. A {@link android.widget.QuickContactBadge} is a widget that
55    initially appears as a thumbnail image. Although you can use any {@link android.graphics.Bitmap}
56    for the thumbnail image, you usually use a {@link android.graphics.Bitmap} decoded from the
57    contact's photo thumbnail image.
58</p>
59<p>
60    The small image acts as a control; when users click on the image, the
61    {@link android.widget.QuickContactBadge} expands into a dialog containing the following:
62</p>
63<dl>
64    <dt>A large image</dt>
65    <dd>
66        The large image associated with the contact, or no image is available, a placeholder
67        graphic.
68    </dd>
69    <dt>
70        App icons
71    </dt>
72    <dd>
73        An app icon for each piece of detail data that can be handled by a built-in app. For
74        example, if the contact's details include one or more email addresses, an email icon
75        appears. When users click the icon, all of the contact's email addresses appear. When users
76        click one of the addresses, the email app displays a screen for composing a message to the
77        selected email address.
78    </dd>
79</dl>
80<p>
81    The {@link android.widget.QuickContactBadge} view provides instant access to a contact's
82    details, as well as a fast way of communicating with the contact. Users don't have to look up
83    a contact, find and copy information, and then paste it into the appropriate app. Instead, they
84    can click on the {@link android.widget.QuickContactBadge}, choose the communication method they
85    want to use, and send the information for that method directly to the appropriate app.
86</p>
87<h2 id="AddView">Add a QuickContactBadge View</h2>
88<p>
89    To add a {@link android.widget.QuickContactBadge}, insert a
90    <code>&lt;QuickContactBadge&gt;</code> element in your layout. For example:
91</p>
92<pre>
93&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
94                android:layout_width="match_parent"
95                android:layout_height="match_parent"&gt;
96...
97    &lt;QuickContactBadge
98               android:id=&#64;+id/quickbadge
99               android:layout_height="wrap_content"
100               android:layout_width="wrap_content"
101               android:scaleType="centerCrop"/&gt;
102    ...
103&lt;/RelativeLayout&gt;
104</pre>
105<h2 id="">Retrieve provider data</h2>
106<p>
107    To display a contact in the {@link android.widget.QuickContactBadge}, you need a content URI
108    for the contact and a {@link android.graphics.Bitmap} for the small image. You generate
109    both the content URI and the {@link android.graphics.Bitmap} from columns retrieved from the
110    Contacts Provider. Specify these columns as part of the projection you use to load data into
111    your {@link android.database.Cursor}.
112</p>
113<p>
114    For Android 3.0 (API level 11) and later, include the following columns in your projection:</p>
115<ul>
116    <li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
117    <li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
118    <li>
119        {@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI
120        Contacts.PHOTO_THUMBNAIL_URI}
121    </li>
122</ul>
123<p>
124    For Android 2.3.3 (API level 10) and earlier, use the following columns:
125</p>
126<ul>
127    <li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
128    <li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
129</ul>
130<p>
131    The remainder of this lesson assumes that you've already loaded a
132    {@link android.database.Cursor} that contains these columns as well as others you may have
133    chosen. To learn how to retrieve this columns in a {@link android.database.Cursor}, read the
134    lesson <a href="retrieve-names.html">Retrieving a List of Contacts</a>.
135</p>
136<h2 id="SetURIThumbnail">Set the Contact URI and Thumbnail</h2>
137<p>
138    Once you have the necessary columns, you can bind data to the
139    {@link android.widget.QuickContactBadge}.
140</p>
141<h3>Set the Contact URI</h3>
142<p>
143    To set the content URI for the contact, call
144    {@link android.provider.ContactsContract.Contacts#getLookupUri getLookupUri(id,lookupKey)} to
145    get a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, then
146    call {@link android.widget.QuickContactBadge#assignContactUri assignContactUri()} to set the
147    contact. For example:
148</p>
149<pre>
150    // The Cursor that contains contact rows
151    Cursor mCursor;
152    // The index of the _ID column in the Cursor
153    int mIdColumn;
154    // The index of the LOOKUP_KEY column in the Cursor
155    int mLookupKeyColumn;
156    // A content URI for the desired contact
157    Uri mContactUri;
158    // A handle to the QuickContactBadge view
159    QuickContactBadge mBadge;
160    ...
161    mBadge = (QuickContactBadge) findViewById(R.id.quickbadge);
162    /*
163     * Insert code here to move to the desired cursor row
164     */
165    // Gets the _ID column index
166    mIdColumn = mCursor.getColumnIndex(Contacts._ID);
167    // Gets the LOOKUP_KEY index
168    mLookupKeyColumn = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
169    // Gets a content URI for the contact
170    mContactUri =
171            Contacts.getLookupUri(
172                mCursor.getLong(mIdColumn),
173                mCursor.getString(mLookupKeyColumn)
174            );
175    mBadge.assignContactUri(mContactUri);
176</pre>
177<p>
178    When users click the {@link android.widget.QuickContactBadge} icon, the contact's
179    details automatically appear in the dialog.
180</p>
181<h3>Set the photo thumbnail</h3>
182<p>
183    Setting the contact URI for the {@link android.widget.QuickContactBadge} does not automatically
184    load the contact's thumbnail photo. To load the photo, get a URI for the photo from the
185    contact's {@link android.database.Cursor} row, use it to open the file containing the compressed
186    thumbnail photo, and read the file into a {@link android.graphics.Bitmap}.
187</p>
188<p class="note">
189    <strong>Note:</strong> The
190    {@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI} column isn't available
191    in platform versions prior to 3.0. For those versions, you must retrieve the URI
192    from the {@link android.provider.ContactsContract.Contacts.Photo Contacts.Photo} subtable.
193</p>
194<p>
195    First, set up variables for accessing the {@link android.database.Cursor} containing the
196    {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
197    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} columns, as
198    described previously:
199</p>
200<pre>
201    // The column in which to find the thumbnail ID
202    int mThumbnailColumn;
203    /*
204     * The thumbnail URI, expressed as a String.
205     * Contacts Provider stores URIs as String values.
206     */
207    String mThumbnailUri;
208    ...
209    /*
210     * Gets the photo thumbnail column index if
211     * platform version &gt;= Honeycomb
212     */
213    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB) {
214        mThumbnailColumn =
215                mCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
216    // Otherwise, sets the thumbnail column to the _ID column
217    } else {
218        mThumbnailColumn = mIdColumn;
219    }
220    /*
221     * Assuming the current Cursor position is the contact you want,
222     * gets the thumbnail ID
223     */
224    mThumbnailUri = mCursor.getString(mThumbnailColumn);
225    ...
226</pre>
227<p>
228    Define a method that takes photo-related data for the contact and dimensions for the
229    destination view,  and returns the properly-sized thumbnail in a
230    {@link android.graphics.Bitmap}. Start by constructing a URI that points to the
231    thumbnail:
232<p>
233<pre>
234    /**
235     * Load a contact photo thumbnail and return it as a Bitmap,
236     * resizing the image to the provided image dimensions as needed.
237     * @param photoData photo ID Prior to Honeycomb, the contact's _ID value.
238     * For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI.
239     * @return A thumbnail Bitmap, sized to the provided width and height.
240     * Returns null if the thumbnail is not found.
241     */
242    private Bitmap loadContactPhotoThumbnail(String photoData) {
243        // Creates an asset file descriptor for the thumbnail file.
244        AssetFileDescriptor afd = null;
245        // try-catch block for file not found
246        try {
247            // Creates a holder for the URI.
248            Uri thumbUri;
249            // If Android 3.0 or later
250            if (Build.VERSION.SDK_INT
251                    &gt;=
252                Build.VERSION_CODES.HONEYCOMB) {
253                // Sets the URI from the incoming PHOTO_THUMBNAIL_URI
254                thumbUri = Uri.parse(photoData);
255            } else {
256            // Prior to Android 3.0, constructs a photo Uri using _ID
257                /*
258                 * Creates a contact URI from the Contacts content URI
259                 * incoming photoData (_ID)
260                 */
261                final Uri contactUri = Uri.withAppendedPath(
262                        Contacts.CONTENT_URI, photoData);
263                /*
264                 * Creates a photo URI by appending the content URI of
265                 * Contacts.Photo.
266                 */
267                thumbUri =
268                        Uri.withAppendedPath(
269                                contactUri, Photo.CONTENT_DIRECTORY);
270            }
271
272        /*
273         * Retrieves an AssetFileDescriptor object for the thumbnail
274         * URI
275         * using ContentResolver.openAssetFileDescriptor
276         */
277        afd = getActivity().getContentResolver().
278                openAssetFileDescriptor(thumbUri, "r");
279        /*
280         * Gets a file descriptor from the asset file descriptor.
281         * This object can be used across processes.
282         */
283        FileDescriptor fileDescriptor = afd.getFileDescriptor();
284        // Decode the photo file and return the result as a Bitmap
285        // If the file descriptor is valid
286        if (fileDescriptor != null) {
287            // Decodes the bitmap
288            return BitmapFactory.decodeFileDescriptor(
289                    fileDescriptor, null, null);
290            }
291        // If the file isn't found
292        } catch (FileNotFoundException e) {
293            /*
294             * Handle file not found errors
295             */
296        // In all cases, close the asset file descriptor
297        } finally {
298            if (afd != null) {
299                try {
300                    afd.close();
301                } catch (IOException e) {}
302            }
303        }
304        return null;
305    }
306</pre>
307<p>
308    Call the <code>loadContactPhotoThumbnail()</code> method in your code to get the
309    thumbnail {@link android.graphics.Bitmap}, and use the result to set the photo thumbnail in
310    your {@link android.widget.QuickContactBadge}:
311</p>
312<pre>
313    ...
314    /*
315     * Decodes the thumbnail file to a Bitmap.
316     */
317    Bitmap mThumbnail =
318            loadContactPhotoThumbnail(mThumbnailUri);
319    /*
320     * Sets the image in the QuickContactBadge
321     * QuickContactBadge inherits from ImageView, so
322     */
323    mBadge.setImageBitmap(mThumbnail);
324</pre>
325<h2 id="ListView">Add a QuickContactBadge to a ListView</h2>
326<p>
327    A {@link android.widget.QuickContactBadge} is a useful addition to a
328    {@link android.widget.ListView} that displays a list of contacts. Use the
329    {@link android.widget.QuickContactBadge} to display a thumbnail photo for each contact; when
330    users click the thumbnail, the {@link android.widget.QuickContactBadge} dialog appears.
331</p>
332<h3>Add the QuickContactBadge element</h3>
333<p>
334    To start, add a {@link android.widget.QuickContactBadge} view element to your item layout
335    For example, if you want to display a {@link android.widget.QuickContactBadge} and a name for
336    each contact you retrieve, put the following XML into a layout file:
337</p>
338<pre>
339&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
340                android:layout_width="match_parent"
341                android:layout_height="wrap_content"&gt;
342    &lt;QuickContactBadge
343        android:id="&#64;+id/quickcontact"
344        android:layout_height="wrap_content"
345        android:layout_width="wrap_content"
346        android:scaleType="centerCrop"/&gt;
347    &lt;TextView android:id="&#64;+id/displayname"
348              android:layout_width="match_parent"
349              android:layout_height="wrap_content"
350              android:layout_toRightOf="&#64;+id/quickcontact"
351              android:gravity="center_vertical"
352              android:layout_alignParentRight="true"
353              android:layout_alignParentTop="true"/&gt;
354&lt;/RelativeLayout&gt;
355</pre>
356<p>
357    In the following sections, this file is referred to as <code>contact_item_layout.xml</code>.
358</p>
359<h3>Set up a custom CursorAdapter</h3>
360<p>
361    To bind a {@link android.support.v4.widget.CursorAdapter} to a {@link android.widget.ListView}
362    containing a {@link android.widget.QuickContactBadge}, define a custom adapter that
363    extends {@link android.support.v4.widget.CursorAdapter}. This approach allows you to process the
364    data in the {@link android.database.Cursor} before you bind it to the
365    {@link android.widget.QuickContactBadge}. This approach also allows you to bind multiple
366    {@link android.database.Cursor} columns to the {@link android.widget.QuickContactBadge}. Neither
367    of these operations is possible in a regular {@link android.support.v4.widget.CursorAdapter}.
368</p>
369<p>
370    The subclass of {@link android.support.v4.widget.CursorAdapter} that you define must
371    override the following methods:
372</p>
373<dl>
374    <dt>{@link android.support.v4.widget.CursorAdapter#newView CursorAdapter.newView()}</dt>
375    <dd>
376        Inflates a new {@link android.view.View} object to hold the item layout. In the override
377        of this method, store handles to the child {@link android.view.View} objects of the layout,
378        including the child {@link android.widget.QuickContactBadge}. By taking this approach, you
379        avoid having to get handles to the child {@link android.view.View} objects each time you
380        inflate a new layout.
381        <p>
382            You must override this method so you can get handles to the individual child
383            {@link android.view.View} objects. This technique allows you to control their binding in
384            {@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}.
385        </p>
386    </dd>
387    <dt>{@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}</dt>
388    <dd>
389        Moves data from the current {@link android.database.Cursor} row to the child
390        {@link android.view.View} objects of the item layout. You must override this method so
391        you can bind both the contact's URI and thumbnail to the
392        {@link android.widget.QuickContactBadge}. The default implementation only allows a 1-to-1
393        mapping between a column and a {@link android.view.View}
394    </dd>
395</dl>
396<p>
397    The following code snippet contains an example of a custom subclass of
398    {@link android.support.v4.widget.CursorAdapter}:
399</p>
400<h3>Define the custom list adapter</h3>
401<p>
402    Define the subclass of {@link android.support.v4.widget.CursorAdapter} including its
403    constructor, and override
404    {@link android.support.v4.widget.CursorAdapter#newView newView()} and
405    {@link android.support.v4.widget.CursorAdapter#bindView bindView()}:
406</p>
407<pre>
408    /**
409     *
410     *
411     */
412    private class ContactsAdapter extends CursorAdapter {
413        private LayoutInflater mInflater;
414        ...
415        public ContactsAdapter(Context context) {
416            super(context, null, 0);
417
418            /*
419             * Gets an inflater that can instantiate
420             * the ListView layout from the file.
421             */
422            mInflater = LayoutInflater.from(context);
423            ...
424        }
425        ...
426        /**
427         * Defines a class that hold resource IDs of each item layout
428         * row to prevent having to look them up each time data is
429         * bound to a row.
430         */
431        private class ViewHolder {
432            TextView displayname;
433            QuickContactBadge quickcontact;
434        }
435        ..
436        &#64;Override
437        public View newView(
438                Context context,
439                Cursor cursor,
440                ViewGroup viewGroup) {
441            /* Inflates the item layout. Stores resource IDs in a
442             * in a ViewHolder class to prevent having to look
443             * them up each time bindView() is called.
444             */
445            final View itemView =
446                    mInflater.inflate(
447                            R.layout.contact_list_layout,
448                            viewGroup,
449                            false
450                    );
451            final ViewHolder holder = new ViewHolder();
452            holder.displayname =
453                    (TextView) view.findViewById(R.id.displayname);
454            holder.quickcontact =
455                    (QuickContactBadge)
456                            view.findViewById(R.id.quickcontact);
457            view.setTag(holder);
458            return view;
459        }
460        ...
461        &#64;Override
462        public void bindView(
463                View view,
464                Context context,
465                Cursor cursor) {
466            final ViewHolder holder = (ViewHolder) view.getTag();
467            final String photoData =
468                    cursor.getString(mPhotoDataIndex);
469            final String displayName =
470                    cursor.getString(mDisplayNameIndex);
471            ...
472            // Sets the display name in the layout
473            holder.displayname = cursor.getString(mDisplayNameIndex);
474            ...
475            /*
476             * Generates a contact URI for the QuickContactBadge.
477             */
478            final Uri contactUri = Contacts.getLookupUri(
479                    cursor.getLong(mIdIndex),
480                    cursor.getString(mLookupKeyIndex));
481            holder.quickcontact.assignContactUri(contactUri);
482            String photoData = cursor.getString(mPhotoDataIndex);
483            /*
484             * Decodes the thumbnail file to a Bitmap.
485             * The method loadContactPhotoThumbnail() is defined
486             * in the section "Set the Contact URI and Thumbnail"
487             */
488            Bitmap thumbnailBitmap =
489                    loadContactPhotoThumbnail(photoData);
490            /*
491             * Sets the image in the QuickContactBadge
492             * QuickContactBadge inherits from ImageView
493             */
494            holder.quickcontact.setImageBitmap(thumbnailBitmap);
495    }
496</pre>
497
498<h3>Set up variables</h3>
499<p>
500    In your code, set up variables, including a {@link android.database.Cursor} projection that
501    includes the necessary columns.
502</p>
503<p class="note">
504    <strong>Note:</strong> The following code snippets use the method
505    <code>loadContactPhotoThumbnail()</code>, which is defined in the section
506    <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
507</p>
508<p>
509    For example:
510</p>
511<pre>
512public class ContactsFragment extends Fragment implements
513        LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
514...
515    // Defines a ListView
516    private ListView mListView;
517    // Defines a ContactsAdapter
518    private ContactsAdapter mAdapter;
519    ...
520    // Defines a Cursor to contain the retrieved data
521    private Cursor mCursor;
522    /*
523     * Defines a projection based on platform version. This ensures
524     * that you retrieve the correct columns.
525     */
526    private static final String[] PROJECTION =
527            {
528                Contacts._ID,
529                Contacts.LOOKUP_KEY,
530                (Build.VERSION.SDK_INT &gt;=
531                 Build.VERSION_CODES.HONEYCOMB) ?
532                        Contacts.DISPLAY_NAME_PRIMARY :
533                        Contacts.DISPLAY_NAME
534                (Build.VERSION.SDK_INT &gt;=
535                 Build.VERSION_CODES.HONEYCOMB) ?
536                        Contacts.PHOTO_THUMBNAIL_ID :
537                        /*
538                         * Although it's not necessary to include the
539                         * column twice, this keeps the number of
540                         * columns the same regardless of version
541                         */
542                        Contacts_ID
543                ...
544            };
545    /*
546     * As a shortcut, defines constants for the
547     * column indexes in the Cursor. The index is
548     * 0-based and always matches the column order
549     * in the projection.
550     */
551    // Column index of the _ID column
552    private int mIdIndex = 0;
553    // Column index of the LOOKUP_KEY column
554    private int mLookupKeyIndex = 1;
555    // Column index of the display name column
556    private int mDisplayNameIndex = 3;
557    /*
558     * Column index of the photo data column.
559     * It's PHOTO_THUMBNAIL_URI for Honeycomb and later,
560     * and _ID for previous versions.
561     */
562    private int mPhotoDataIndex =
563            Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB ?
564            3 :
565            0;
566    ...
567</pre>
568<h3>Set up the ListView</h3>
569<p>
570    In {@link android.support.v4.app.Fragment#onCreate Fragment.onCreate()}, instantiate the custom
571    cursor adapter and get a handle to the {@link android.widget.ListView}:
572</p>
573<pre>
574    &#64;Override
575    public void onCreate(Bundle savedInstanceState) {
576        ...
577        /*
578         * Instantiates the subclass of
579         * CursorAdapter
580         */
581        ContactsAdapter mContactsAdapter =
582                new ContactsAdapter(getActivity());
583        /*
584         * Gets a handle to the ListView in the file
585         * contact_list_layout.xml
586         */
587        mListView = (ListView) findViewById(R.layout.contact_list_layout);
588        ...
589    }
590    ...
591</pre>
592<p>
593    In {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, bind the
594    <code>ContactsAdapter</code> to the {@link android.widget.ListView}:
595</p>
596<pre>
597    &#64;Override
598    public void onActivityCreated(Bundle savedInstanceState) {
599        ...
600        // Sets up the adapter for the ListView
601        mListView.setAdapter(mAdapter);
602        ...
603    }
604    ...
605</pre>
606<p>
607    When you get back a {@link android.database.Cursor} containing the contacts data, usually in
608    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()},
609    call {@link android.support.v4.widget.CursorAdapter#swapCursor swapCursor()} to move the
610    {@link android.database.Cursor} data to the {@link android.widget.ListView}. This displays the
611    {@link android.widget.QuickContactBadge} for each entry in the list of contacts:
612</p>
613<pre>
614    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
615        // When the loader has completed, swap the cursor into the adapter.
616        mContactsAdapter.swapCursor(cursor);
617    }
618</pre>
619<p>
620    When you bind a {@link android.database.Cursor} to a
621    {@link android.widget.ListView} with a {@link android.support.v4.widget.CursorAdapter}
622    (or subclass), and you use a {@link android.support.v4.content.CursorLoader} to load the
623    {@link android.database.Cursor}, always clear references to the {@link android.database.Cursor}
624    in your implementation of
625    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}.
626    For example:
627</p>
628<pre>
629    &#64;Override
630    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
631        // Removes remaining reference to the previous Cursor
632        mContactsAdapter.swapCursor(null);
633    }
634</pre>
635