クイック コンタクト バッジを表示する

このページでは、QuickContactBadge を UI に追加する方法と、データをバインドする方法について説明します。QuickContactBadge は、最初はサムネイル画像として表示されるウィジェットです。サムネイル画像には任意の Bitmap を使用できますが、通常は連絡先の写真のサムネイル画像からデコードされた Bitmap を使用します。

小さい画像がコントロールとして機能します。ユーザーが画像をタップすると、QuickContactBadge が展開されて、以下を含むダイアログが表示されます。

大きな画像
連絡先に関連付けられた大きな画像、または画像がない場合はプレースホルダ グラフィック。
アプリアイコン
組み込みアプリで処理できる各詳細データのアプリアイコン。たとえば、連絡先の詳細に 1 つ以上のメールアドレスが含まれている場合は、メールアイコンが表示されます。このアイコンをタップすると、連絡先のメールアドレスがすべて表示されます。ユーザーがいずれかのアドレスをタップすると、選択したメールアドレス宛てのメッセージを作成する画面がメールアプリに表示されます。

QuickContactBadge ビューを使用すると、連絡先の詳細にすばやくアクセスして、簡単に連絡を取ることができます。ユーザーは、連絡先を検索して情報を見つけてコピーし、適切なアプリに貼り付ける必要はありません。代わりに、QuickContactBadge をタップして使用する通信方法を選択し、その情報を適切なアプリに直接送信できます。

QuickContactBadge ビューを追加する

QuickContactBadge を追加するには、次の例に示すように、レイアウトに <QuickContactBadge> 要素を挿入します。

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

プロバイダ データを取得する

QuickContactBadge に連絡先を表示するには、連絡先のコンテンツ URI と小さい画像の Bitmap が必要です。コンテンツ URI と Bitmap の両方を、連絡先プロバイダから取得した列から生成します。これらの列を、Cursor にデータを読み込むために使用する射影の一部として指定します。

Android 3.0(API レベル 11)以降の場合は、プロジェクションに次の列を含めます。

Android 2.3.3(API レベル 10)以前の場合は、以下の列を使用します。

このページの例では、これらの列と選択された他の列を含む Cursor が読み込まれていることを前提としています。Cursor 内の列を取得する方法については、連絡先のリストを取得するをご覧ください。

連絡先 URI とサムネイルを設定する

必要な列を作成したら、データを QuickContactBadge にバインドできます。

連絡先 URI を設定する

連絡先のコンテンツ URI を設定するには、getLookupUri(id,lookupKey) を呼び出して CONTENT_LOOKUP_URI を取得し、assignContactUri() を呼び出して連絡先を設定します。次の例はこの処理を示しています。

Kotlin

    // The Cursor that contains contact rows
    var cursor: 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
    ...
    cursor?.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)
        )
        binding.badge.assignContactUri(contactUri)
    }

Java

    // The Cursor that contains contact rows
    Cursor cursor;
    // 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;
    ...
    /*
     * 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 =
            Contacts.getLookupUri(
                cursor.getLong(idColumn),
                cursor.getString(lookupKeyColumn)
            );
    binding.badge.assignContactUri(contactUri);

ユーザーが QuickContactBadge アイコンをタップすると、ダイアログに連絡先の詳細が表示されます。

写真のサムネイルを設定する

QuickContactBadge に連絡先 URI を設定しても、連絡先のサムネイル写真は自動的に読み込まれません。写真を読み込むには、連絡先の Cursor 行から写真の URI を取得し、それを使用して圧縮されたサムネイル写真を含むファイルを開き、ファイルを Bitmap に読み込みます。

注: PHOTO_THUMBNAIL_URI 列は、3.0 より前のバージョンのプラットフォームでは使用できません。そのようなバージョンでは、Contacts.Photo サブテーブルから URI を取得する必要があります。

まず、Contacts._ID 列と Contacts.LOOKUP_KEY 列を含む Cursor にアクセスする変数を設定します。

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
    ...
    cursor?.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);
    ...

連絡先の写真関連のデータと宛先ビューのサイズを取得し、適切なサイズのサムネイルを Bitmap に返すメソッドを定義します。まず、サムネイルを指す URI を作成します。

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 ->
                // Decodes the photo file and returns 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();
        // Decodes the photo file and returns 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;
    }

コード内で loadContactPhotoThumbnail() メソッドを呼び出してサムネイル Bitmap を取得し、その結果を使用して 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.
             */
            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.
     */
    badge.setImageBitmap(mThumbnail);

QuickContactBadge を ListView に追加する

QuickContactBadge は、連絡先のリストを表示する ListView の便利な追加機能です。QuickContactBadge を使用して各連絡先のサムネイル写真を表示します。ユーザーがサムネイルをタップすると、QuickContactBadge ダイアログが表示されます。

QuickContactBadge 要素を追加する

まず、アイテム レイアウトに QuickContactBadge ビュー要素を追加します。たとえば、取得する連絡先ごとに QuickContactBadge と名前を表示する場合は、次の XML をレイアウト ファイルに含めます。

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

以降のセクションでは、このファイルを contact_item_layout.xml とします。

カスタム CursorAdapter をセットアップする

QuickContactBadge を含む ListViewCursorAdapter をバインドするには、CursorAdapter を拡張するカスタム アダプターを定義します。この方法では、QuickContactBadge にバインドする前に Cursor 内のデータを処理できます。この方法では、複数の Cursor 列を QuickContactBadge にバインドすることもできます。これらのオペレーションはいずれも、通常の CursorAdapter では実行できません。

定義した CursorAdapter のサブクラスは、次のメソッドをオーバーライドする必要があります。

CursorAdapter.newView()
新しい View オブジェクトをインフレートして、アイテムのレイアウトを保持します。このメソッドのオーバーライドに、レイアウトの子 View オブジェクト(子 QuickContactBadge を含む)に対するハンドルを保存します。この方法により、新しいレイアウトをインフレートするたびに子 View オブジェクトに対するハンドルを取得する必要がなくなります。

個々の子 View オブジェクトに対するハンドルを取得できるように、このメソッドをオーバーライドする必要があります。この方法では、CursorAdapter.bindView() でバインディングを制御できます。

CursorAdapter.bindView()
データを現在の Cursor 行からアイテム レイアウトの子 View オブジェクトに移動します。連絡先の URI とサムネイルの両方を QuickContactBadge にバインドできるように、このメソッドをオーバーライドする必要があります。デフォルトの実装では、列と View の間に 1 対 1 のマッピングのみが許可されます。

次のコード スニペットには、CursorAdapter のカスタム サブクラスの例が含まれています。

カスタム リスト アダプターを定義する

CursorAdapter のサブクラス(コンストラクタを含む)を定義し、newView()bindView() をオーバーライドします。

Kotlin

    /**
     * Defines a class that holds 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 view references
             * in a ViewHolder class to prevent having to look
             * them up each time bindView() is called.
             */
            return ContactListLayoutBinding.inflate(inflater,
                    viewGroup,
                    false).also { binding ->
                view.tag = ViewHolder().apply {
                    displayname = binding.displayname
                    quickcontact = binding.quickcontact
                }
            }.root
        }
        ...
        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 inflater;
        ...
        public ContactsAdapter(Context context) {
            super(context, null, 0);

            /*
             * Gets an inflater that can instantiate
             * the ListView layout from the file
             */
            inflater = LayoutInflater.from(context);
            ...
        }
        ...
        /**
         * Defines a class that holds 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 view references
             * in a ViewHolder class to prevent having to look
             * them up each time bindView() is called.
             */
            final ContactListLayoutBinding binding =
            ContactListLayoutBinding.inflate(inflater, 
                viewGroup,
                false);
            final ViewHolder holder = new ViewHolder();
            holder.displayname =
                    binding.displayName;
            holder.quickcontact =
                    binding.quickContact;
            view.setTag(holder);
            return binding.root;
        }
        ...
        @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);
    }

変数をセットアップする

次の例に示すように、コード内で、必要な列を含む Cursor 射影を含む変数を設定します。

注: 次のコード スニペットでは、連絡先 URI とサムネイルを設定するのセクションで定義されている loadContactPhotoThumbnail() メソッドを使用しています。

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

ListView をセットアップする

Fragment.onCreate() で、カスタム カーソル アダプターをインスタンス化し、ListView に対するハンドルを取得します。

Kotlin

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

Java

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

onViewCreated() で、ContactsAdapterListView にバインドします。

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, 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 onViewCreated(@NonNull View view, @Nullable 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);
        }
        ...
    }
    ...

連絡先データを含む Cursor が返されたら(通常は onLoadFinished() 内)、swapCursor() を呼び出して Cursor データを ListView に移動します。これにより、連絡先リストの各エントリに QuickContactBadge が表示されます。

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);
    }

CursorAdapter(またはサブクラス)で CursorListView にバインドし、CursorLoader を使用して Cursor を読み込む場合は、必ず onLoaderReset() の実装で Cursor への参照をクリアします。次の例はこの処理を示しています。

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);
    }