The Android Developer Challenge is back! Submit your idea before December 2.

Menampilkan badge kontak kilat

Pelajaran ini menjelaskan cara menambahkan QuickContactBadge ke UI dan cara mengaitkan data ke sana. QuickContactBadge adalah widget yang awalnya muncul sebagai thumbnail. Meskipun dapat menggunakan Bitmap untuk thumbnail, biasanya Anda menggunakan Bitmap yang didekodekan dari thumbnail foto kontak.

Gambar kecil ini berfungsi sebagai kontrol; saat pengguna mengklik gambar, QuickContactBadge akan meluas menjadi dialog yang berisi:

Gambar besar
Gambar besar yang terkait dengan kontak, atau jika tidak ada gambar yang tersedia, grafis placeholder.
Ikon aplikasi
Ikon aplikasi untuk setiap bagian data detail yang dapat ditangani oleh aplikasi bawaan. Misalnya, jika detail kontak menyertakan satu atau beberapa alamat email, ikon email akan muncul. Saat pengguna mengklik ikon ini, semua alamat email kontak akan muncul. Jika pengguna mengklik salah satu alamat, aplikasi email akan menampilkan layar untuk menulis pesan ke alamat email yang dipilih.

Tampilan QuickContactBadge memberikan akses instan ke detail kontak, serta cara cepat untuk berkomunikasi dengan kontak. Pengguna tidak perlu mencari kontak, menemukan dan menyalin informasi, lalu menempelkannya ke aplikasi yang sesuai. Mereka cukup mengklik QuickContactBadge, memilih metode komunikasi yang ingin digunakan, dan mengirimkan informasi untuk metode tersebut langsung ke aplikasi yang sesuai.

Menambahkan tampilan QuickContactBadge

Untuk menambahkan QuickContactBadge, sisipkan elemen <QuickContactBadge> dalam tata letak. Contoh:

    <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>
    

Mengambil data penyedia

Untuk menampilkan kontak di QuickContactBadge, Anda memerlukan URI konten untuk kontak itu dan sebuah Bitmap untuk gambar kecil. URI konten dan Bitmap dihasilkan dari kolom yang diambil dari Penyedia Kontak. Tetapkan kolom ini sebagai bagian dari proyeksi yang Anda gunakan untuk memuat data ke dalam Cursor.

Untuk Android 3.0 (API level 11) dan yang lebih baru, sertakan kolom berikut dalam proyeksi Anda:

Untuk Android 2.3.3 (API level 10) dan yang lebih lama, gunakan kolom berikut:

Bagian selanjutnya dalam pelajaran ini mengasumsikan bahwa Anda telah memuat Cursor yang berisi kolom-kolom ini, juga kolom lain yang mungkin telah Anda pilih. Untuk mempelajari cara mengambil kolom ini di Cursor, baca pelajaran Mengambil daftar kontak.

Menetapkan URI dan thumbnail kontak

Setelah memiliki kolom yang diperlukan, Anda dapat mengaitkan data ke QuickContactBadge.

Menetapkan URI kontak

Untuk menetapkan URI konten bagi kontak, panggil getLookupUri(id,lookupKey) untuk mendapatkan CONTENT_LOOKUP_URI, lalu panggil assignContactUri() untuk menetapkan kontak. Contoh:

Kotlin

        // The Cursor that contains contact rows
        var mCursor: Cursor? = null
        // The index of the _ID column in the Cursor
        var idColumn: Int = 0
        // The index of the LOOKUP_KEY column in the Cursor
        var lookupKeyColumn: Int = 0
        // A content URI for the desired contact
        var contactUri: Uri? = null
        // A handle to the QuickContactBadge view
        lateinit var mBadge: QuickContactBadge
        ...
        mBadge = findViewById(R.id.quickbadge)

        mCursor?.let { cursor ->
            /*
             * Insert code here to move to the desired cursor row
             */
            // Gets the _ID column index
            idColumn = cursor.getColumnIndex(ContactsContract.Contacts._ID)
            // Gets the LOOKUP_KEY index
            lookupKeyColumn = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)
            // Gets a content URI for the contact
            contactUri = ContactsContract.Contacts.getLookupUri(
                    cursor.getLong(idColumn),
                    cursor.getString(lookupKeyColumn)
            )
            mBadge.assignContactUri(contactUri)
        }
    

Java

        // The Cursor that contains contact rows
        Cursor mCursor;
        // The index of the _ID column in the Cursor
        int idColumn;
        // The index of the LOOKUP_KEY column in the Cursor
        int lookupKeyColumn;
        // A content URI for the desired contact
        Uri contactUri;
        // 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
        idColumn = mCursor.getColumnIndex(ContactsContract.Contacts._ID);
        // Gets the LOOKUP_KEY index
        lookupKeyColumn = mCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
        // Gets a content URI for the contact
        contactUri =
                Contacts.getLookupUri(
                    mCursor.getLong(idColumn),
                    mCursor.getString(lookupKeyColumn)
                );
        mBadge.assignContactUri(contactUri);
    

Saat pengguna mengklik ikon QuickContactBadge, detail kontak akan otomatis muncul di dialog.

Menetapkan thumbnail foto

Menetapkan URI kontak untuk QuickContactBadge tidak otomatis memuat foto thumbnail kontak tersebut. Untuk memuat foto, dapatkan URI untuk foto itu dari baris Cursor kontak, gunakan untuk membuka file yang berisi foto thumbnail yang dikompresi, dan baca file tersebut ke dalam Bitmap.

Catatan: Kolom PHOTO_THUMBNAIL_URI tidak tersedia pada versi platform sebelum 3.0. Untuk versi tersebut, Anda harus mengambil URI dari sub-tabel Contacts.Photo.

Pertama, siapkan variabel untuk mengakses Cursor yang berisi kolom Contacts._ID dan Contacts.LOOKUP_KEY, seperti yang dijelaskan sebelumnya:

Kotlin

        // The column in which to find the thumbnail ID
        var thumbnailColumn: Int = 0
        /*
         * The thumbnail URI, expressed as a String.
         * Contacts Provider stores URIs as String values.
         */
        var thumbnailUri: String? = null
        ...
        mCursor?.let { cursor ->
            /*
             * Gets the photo thumbnail column index if
             * platform version >= Honeycomb
             */
            thumbnailColumn = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI)
                // Otherwise, sets the thumbnail column to the _ID column
            } else {
                idColumn
            }
            /*
             * Assuming the current Cursor position is the contact you want,
             * gets the thumbnail ID
             */
            thumbnailUri = cursor.getString(thumbnailColumn)
        }
    

Java

        // The column in which to find the thumbnail ID
        int thumbnailColumn;
        /*
         * The thumbnail URI, expressed as a String.
         * Contacts Provider stores URIs as String values.
         */
        String thumbnailUri;
        ...
        /*
         * Gets the photo thumbnail column index if
         * platform version >= Honeycomb
         */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            thumbnailColumn =
                    cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI);
        // Otherwise, sets the thumbnail column to the _ID column
        } else {
            thumbnailColumn = idColumn;
        }
        /*
         * Assuming the current Cursor position is the contact you want,
         * gets the thumbnail ID
         */
        thumbnailUri = cursor.getString(thumbnailColumn);
        ...
    

Tentukan metode yang mengambil data terkait foto untuk kontak dan dimensi untuk tampilan tujuan, serta menampilkan thumbnail yang berukuran tepat dalam Bitmap. Mulailah dengan membuat URI yang mengarah ke thumbnail:

Kotlin

        /**
         * 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 fun loadContactPhotoThumbnail(photoData: String): Bitmap? {
            // Creates an asset file descriptor for the thumbnail file.
            var afd: AssetFileDescriptor? = null
            // try-catch block for file not found
            try {
                // Creates a holder for the URI.
                val thumbUri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                    // If Android 3.0 or later
                    // Sets the URI from the incoming PHOTO_THUMBNAIL_URI
                    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)
                     */
                    val contactUri: Uri =
                            Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, photoData)
                    /*
                     * Creates a photo URI by appending the content URI of
                     * Contacts.Photo.
                     */
                    Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY)
                }

                /*
                 * Retrieves an AssetFileDescriptor object for the thumbnail
                 * URI
                 * using ContentResolver.openAssetFileDescriptor
                 */
                afd = activity?.contentResolver?.openAssetFileDescriptor(thumbUri, "r")
                /*
                 * Gets a file descriptor from the asset file descriptor.
                 * This object can be used across processes.
                 */
                return afd?.fileDescriptor?.let {fileDescriptor ->
                    // Decode the photo file and return the result as a Bitmap
                    // If the file descriptor is valid
                    BitmapFactory.decodeFileDescriptor(fileDescriptor, null, null)
                }
            } catch (e: FileNotFoundException) {
                /*
                 * Handle file not found errors
                 */
                null
            } finally {
                // In all cases, close the asset file descriptor
                try {
                    afd?.close()
                } catch (e: IOException) {
                }
            }
        }
    

Java

        /**
         * 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(
                            ContactsContract.Contacts.CONTENT_URI, photoData);
                    /*
                     * Creates a photo URI by appending the content URI of
                     * Contacts.Photo.
                     */
                    thumbUri =
                            Uri.withAppendedPath(
                                    contactUri, ContactsContract.Contacts.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;
        }
    

Panggil metode loadContactPhotoThumbnail() dalam kode Anda untuk mendapatkan Bitmap thumbnail, lalu gunakan hasilnya untuk menetapkan thumbnail foto dalam QuickContactBadge:

Kotlin

        ...
        /*
         * Decodes the thumbnail file to a Bitmap.
         */
        mThumbnailUri?.also { thumbnailUri ->
            loadContactPhotoThumbnail(thumbnailUri).also { thumbnail ->
                /*
                 * Sets the image in the QuickContactBadge
                 * QuickContactBadge inherits from ImageView, so
                 */
                badge.setImageBitmap(thumbnail)
            }
        }
    

Java

        ...
        /*
         * Decodes the thumbnail file to a Bitmap.
         */
        Bitmap mThumbnail =
                loadContactPhotoThumbnail(thumbnailUri);
        /*
         * Sets the image in the QuickContactBadge
         * QuickContactBadge inherits from ImageView, so
         */
        badge.setImageBitmap(mThumbnail);
    

Menambahkan QuickContactBadge ke ListView

QuickContactBadge adalah tambahan berguna untuk ListView yang menampilkan daftar kontak. Gunakan QuickContactBadge untuk menampilkan foto thumbnail setiap kontak. Ketika pengguna mengklik thumbnail, dialog QuickContactBadge akan muncul.

Menambahkan elemen QuickContactBadge

Untuk memulai, tambahkan elemen tampilan QuickContactBadge ke tata letak item Anda. Misalnya, jika Anda ingin menampilkan QuickContactBadge dan nama untuk setiap kontak yang Anda ambil, masukkan XML berikut ke dalam file tata letak:

    <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>
    

Di bagian selanjutnya, file ini disebut contact_item_layout.xml.

Menyiapkan CursorAdapter kustom

Untuk mengaitkan CursorAdapter ke ListView yang berisi QuickContactBadge, tentukan adaptor kustom yang memperluas CursorAdapter. Pendekatan ini memungkinkan Anda memproses data di Cursor sebelum mengaitkannya ke QuickContactBadge. Pendekatan ini juga memungkinkan Anda mengaitkan beberapa kolom Cursor ke QuickContactBadge. Semua operasi ini tidak dimungkinkan di CursorAdapter reguler.

Subclass CursorAdapter yang Anda tentukan harus mengganti metode berikut:

CursorAdapter.newView()
Meluaskan objek View baru untuk menampung tata letak item. Dalam penggantian metode ini, simpan handle ke objek View turunan tata letak, termasuk QuickContactBadge turunannya. Dengan pendekatan ini, Anda tidak perlu memperoleh handle ke objek View turunan setiap kali meluaskan tata letak baru.

Anda harus mengganti metode ini agar dapat memperoleh handle untuk objek View turunan tertentu. Teknik ini memungkinkan Anda mengontrol binding di CursorAdapter.bindView().

CursorAdapter.bindView()
Memindahkan data dari baris Cursor saat ini ke objek View turunan tata letak item. Anda harus mengganti metode ini agar dapat mengaitkan URI dan thumbnail kontak ke QuickContactBadge. Implementasi defaultnya hanya memungkinkan pemetaan 1 lawan 1 antara kolom dan View.

Cuplikan kode berikut berisi contoh subclass CursorAdapter kustom:

Menentukan adaptor daftar kustom

Tentukan subclass CursorAdapter termasuk constructor-nya, dan ganti newView() dan bindView():

Kotlin

        /**
         * 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 data class ViewHolder(
                internal var displayname: TextView? = null,
                internal var quickcontact: QuickContactBadge? = null
        )

        /**
         *
         *
         */
        private inner class ContactsAdapter(
                context: Context,
                val inflater: LayoutInflater = LayoutInflater.from(context)
        ) : CursorAdapter(context, null, 0) {
            ...
            override fun newView(
                    context: Context,
                    cursor: Cursor,
                    viewGroup: ViewGroup
            ): View {
                /* 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.
                 */
                return inflater.inflate(
                        R.layout.contact_list_layout,
                        viewGroup,
                        false
                ).also { view ->
                    view.tag = ViewHolder().apply {
                        displayname = view.findViewById(R.id.displayname)
                        quickcontact = view.findViewById(R.id.quickcontact)
                    }
                }
            }

            ...

            override fun bindView(view: View?, context: Context?, cursor: Cursor?) {
                (view?.tag as? ViewHolder)?.also { holder ->
                    cursor?.apply {
                        ...
                        // Sets the display name in the layout
                        holder.displayname?.text = getString(displayNameIndex)
                        ...
                        /*
                         * Generates a contact URI for the QuickContactBadge.
                         */
                        ContactsContract.Contacts.getLookupUri(
                                getLong(idIndex),
                                cursor.getString(lookupKeyIndex)
                        ).also { contactUri ->
                            holder.quickcontact?.assignContactUri(contactUri)
                        }

                        getString(photoDataIndex)?.also {photoData ->
                            /*
                             * Decodes the thumbnail file to a Bitmap.
                             * The method loadContactPhotoThumbnail() is defined
                             * in the section "Set the Contact URI and Thumbnail"
                             */
                            loadContactPhotoThumbnail(photoData)?.also { thumbnailBitmap ->
                                /*
                                 * Sets the image in the QuickContactBadge
                                 * QuickContactBadge inherits from ImageView
                                 */
                                holder.quickcontact?.setImageBitmap(thumbnailBitmap)
                            }
                        }
                    }
                }

            }
        }
    

Java

        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(photoDataIndex);
                final String displayName =
                        cursor.getString(displayNameIndex);
                ...
                // Sets the display name in the layout
                holder.displayname = cursor.getString(displayNameIndex);
                ...
                /*
                 * Generates a contact URI for the QuickContactBadge.
                 */
                final Uri contactUri = Contacts.getLookupUri(
                        cursor.getLong(idIndex),
                        cursor.getString(lookupKeyIndex));
                holder.quickcontact.assignContactUri(contactUri);
                String photoData = cursor.getString(photoDataIndex);
                /*
                 * 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);
        }
    

Menyiapkan variabel

Dalam kode Anda, siapkan variabel, termasuk proyeksi Cursor yang mencakup kolom-kolom yang diperlukan.

Catatan: Cuplikan kode berikut menggunakan metode loadContactPhotoThumbnail(), yang ditentukan di bagian Menetapkan URI dan thumbnail kontak.

Contoh:

Kotlin

    /*
     * Defines a projection based on platform version. This ensures
     * that you retrieve the correct columns.
     */
    private val PROJECTION: Array<out String> = arrayOf(
            ContactsContract.Contacts._ID,
            ContactsContract.Contacts.LOOKUP_KEY,
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
            } else {
                ContactsContract.Contacts.DISPLAY_NAME
            },
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                ContactsContract.Contacts.PHOTO_FILE_ID
            } else {
                /*
                 * Although it's not necessary to include the
                 * column twice, this keeps the number of
                 * columns the same regardless of version
                 */
                ContactsContract.Contacts._ID
            }
    )
    ...
    class ContactsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
        ...
        // Defines a ListView
        private val listView: ListView? = null
        // Defines a ContactsAdapter
        private val adapter: ContactsAdapter? = null
        ...
        // Defines a Cursor to contain the retrieved data
        private val cursor: Cursor? = null
        /*
         * 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 val idIndex = 0
        // Column index of the LOOKUP_KEY column
        private val lookupKeyIndex = 1
        // Column index of the display name column
        private val displayNameIndex = 3
        /*
         * Column index of the photo data column.
         * It's PHOTO_THUMBNAIL_URI for Honeycomb and later,
         * and _ID for previous versions.
         */
        private val photoDataIndex: Int =
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) 3 else 0
        ...
    

Java

    public class ContactsFragment extends Fragment implements
            LoaderManager.LoaderCallbacks<Cursor> {
    ...
        // Defines a ListView
        private ListView listView;
        // Defines a ContactsAdapter
        private ContactsAdapter adapter;
        ...
        // Defines a Cursor to contain the retrieved data
        private Cursor cursor;
        /*
         * Defines a projection based on platform version. This ensures
         * that you retrieve the correct columns.
         */
        private static final String[] PROJECTION =
                {
                    ContactsContract.Contacts._ID,
                    ContactsContract.Contacts.LOOKUP_KEY,
                    (Build.VERSION.SDK_INT >=
                     Build.VERSION_CODES.HONEYCOMB) ?
                            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
                            ContactsContract.Contacts.DISPLAY_NAME
                    (Build.VERSION.SDK_INT >=
                     Build.VERSION_CODES.HONEYCOMB) ?
                            ContactsContract.Contacts.PHOTO_FILE_ID :
                            /*
                             * Although it's not necessary to include the
                             * column twice, this keeps the number of
                             * columns the same regardless of version
                             */
                            ContactsContract.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 idIndex = 0;
        // Column index of the LOOKUP_KEY column
        private int lookupKeyIndex = 1;
        // Column index of the display name column
        private int displayNameIndex = 3;
        /*
         * Column index of the photo data column.
         * It's PHOTO_THUMBNAIL_URI for Honeycomb and later,
         * and _ID for previous versions.
         */
        private int photoDataIndex =
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
                3 :
                0;
        ...
    

Menyiapkan ListView

Di Fragment.onCreate(), mulai instance adaptor cursor kustom dan dapatkan handle ke ListView:

Kotlin

        override fun onCreateView(
                inflater: LayoutInflater,
                container: ViewGroup?,
                savedInstanceState: Bundle?
        ): View? {
            return inflater.inflate(...).let { view ->
                ...
                /*
                 * Gets a handle to the ListView in the file
                 * contact_list_layout.xml
                 */
                listView = view.findViewById<ListView>(R.id.contact_list)
                mAdapter?.also {
                    listView?.adapter = it
                }
                ...
            }
        }
        ...
    

Java

        @Override
        public View onCreateView(LayoutInflater inflater,
                ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(...)
            ...
            /*
             * Gets a handle to the ListView in the file
             * contact_list_layout.xml
             */
            listView = (ListView) view.findViewById(R.id.contact_list_view);
            if (listView != null && adapter != null) {
                listView.setAdapter(adapter);
            }
            ...
        }
        ...
    

Di onActivityCreated(), kaitkan ContactsAdapter ke ListView:

Kotlin

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        /*
         * Instantiates the subclass of
         * CursorAdapter
         */
        mAdapter = activity?.let {
            ContactsAdapter(it).also { adapter ->
                // Sets up the adapter for the ListView
                listView?.adapter = adapter
            }
        }
    }
    

Java

    @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            ...
            /*
             * Instantiates the subclass of
             * CursorAdapter
             */
            mAdapter = new ContactsAdapter(getActivity());
            // Sets up the adapter for the ListView
            if (listView != null && mAdapter != null) {
                listView.setAdapter(mAdapter);
            }
            ...
        }
        ...
    

Saat Anda mendapatkan kembali Cursor yang berisi data kontak, biasanya di onLoadFinished(), panggil swapCursor() untuk memindahkan data Cursor ke ListView. Cara ini akan menampilkan QuickContactBadge untuk setiap entri dalam daftar kontak:

Kotlin

    override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
        // When the loader has completed, swap the cursor into the adapter.
        mAdapter?.swapCursor(cursor)
    }
    

Java

    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
            // When the loader has completed, swap the cursor into the adapter.
            mAdapter.swapCursor(cursor);
        }
    

Saat Anda mengaitkan Cursor ke ListView dengan CursorAdapter (atau subclass), dan Anda menggunakan CursorLoader untuk memuat Cursor, selalu hapus referensi ke Cursor dalam implementasi onLoaderReset(). Contoh:

Kotlin

        override fun onLoaderReset(loader: Loader<Cursor>) {
            // Removes remaining reference to the previous Cursor
            adapter?.swapCursor(null)
        }
    

Java

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
            // Removes remaining reference to the previous Cursor
            adapter.swapCursor(null);
        }