빠른 연락처 배지 표시

이 페이지에서는 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)
    }

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

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

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

        }
    }

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