빠른 연락처 배지 표시

이 페이지에서는 UI에 QuickContactBadge를 추가하고 데이터를 바인딩하는 방법을 보여줍니다. QuickContactBadge는 처음에 썸네일 이미지로 표시되는 위젯입니다. 미리보기 이미지에는 모든 Bitmap을 사용할 수 있지만 일반적으로 연락처의 사진 미리보기 이미지에서 디코딩된 Bitmap을 사용합니다.

작은 이미지가 컨트롤 역할을 합니다. 사용자가 이미지를 탭하면 QuickContactBadge는 다음 항목이 포함된 대화상자로 확장됩니다.

큰 이미지
연락처와 관련된 큰 이미지 또는 사용할 수 있는 이미지가 없는 경우 자리표시자 그래픽입니다.
앱 아이콘
내장 앱에서 처리할 수 있는 각 세부정보 데이터와 관련된 앱 아이콘입니다. 예를 들어 연락처 세부정보에 이메일 주소가 한 개 이상 포함되어 있는 경우 이메일 아이콘이 표시됩니다. 사용자가 아이콘을 탭하면 연락처의 이메일 주소가 모두 표시됩니다. 사용자가 주소 중 하나를 탭하면 이메일 앱에서 선택한 이메일 주소로 메시지를 작성하는 화면을 표시합니다.

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

자바

    // 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._IDContacts.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)
    }

자바

    // 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) {
            }
        }
    }

자바

    /**
     * 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)
        }
    }

자바

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

ListView에 QuickContactBadge 추가

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

CursorAdapterQuickContactBadge가 포함된 ListView에 바인딩하려면 CursorAdapter를 확장하는 맞춤 어댑터를 정의하세요. 이 접근 방식을 사용하면 QuickContactBadge에 바인딩하기 전에 Cursor에 있는 데이터를 처리할 수 있습니다. 이 접근 방식을 사용하면 여러 Cursor 열을 QuickContactBadge에 결합할 수도 있습니다. 이러한 작업은 둘 다 일반적인 CursorAdapter에서는 실행할 수 없습니다.

정의한 CursorAdapter의 서브클래스는 다음 메서드를 재정의해야 합니다.

CursorAdapter.newView()
View 객체를 확장하여 항목 레이아웃을 포함하도록 하는 메서드입니다. 이 메서드를 재정의할 때 하위 QuickContactBadge를 포함한 레이아웃의 하위 View 객체에 핸들을 저장하세요. 이 방법을 사용하면 새 레이아웃을 확장할 때마다 하위 View 객체에 핸들을 가져오도록 하지 않아도 됩니다.

개별 하위 View 객체로 핸들을 가져올 수 있도록 이 메서드를 재정의해야 합니다. 이 기법을 사용하면 CursorAdapter.bindView()에서 바인딩을 제어할 수 있습니다.

CursorAdapter.bindView()
현재 Cursor 행의 데이터를 항목 레이아웃의 하위 View 객체로 이동합니다. 연락처의 URI와 미리보기 이미지를 둘 다 QuickContactBadge에 바인딩할 수 있도록 이 메서드를 재정의해야 합니다. 기본 구현에서는 열과 View 간의 일대일 매핑만 허용합니다.

다음 코드 스니펫에는 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)
                        }
                    }
                }
            }

        }
    }

자바

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

자바

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

자바

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

자바

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

자바

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

CursorCursorAdapter(또는 서브클래스)가 포함된 ListView에 바인딩하고 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);
    }