Hiện huy hiệu Liên hệ nhanh

Trang này hướng dẫn bạn cách thêm QuickContactBadge vào giao diện người dùng và cách liên kết dữ liệu với dữ liệu đó. QuickContactBadge là một tiện ích ban đầu sẽ xuất hiện dưới dạng hình thu nhỏ. Mặc dù bạn có thể sử dụng bất kỳ Bitmap nào cho hình thu nhỏ, bạn thường sử dụng Bitmap được giải mã từ ảnh thu nhỏ của địa chỉ liên hệ.

Hình ảnh nhỏ đóng vai trò là một thành phần điều khiển; khi người dùng nhấn vào hình ảnh, QuickContactBadge sẽ mở rộng thành một hộp thoại chứa những nội dung sau:

Hình ảnh lớn
Hình ảnh lớn được liên kết với người liên hệ hoặc nếu không có hình ảnh, thì là một hình ảnh giữ chỗ.
Biểu tượng ứng dụng
Biểu tượng ứng dụng cho mỗi phần dữ liệu chi tiết mà ứng dụng tích hợp sẵn có thể xử lý. Để ví dụ: nếu thông tin của người liên hệ bao gồm một hoặc nhiều địa chỉ email, biểu tượng email sẽ xuất hiện. Khi người dùng nhấn vào biểu tượng này, tất cả địa chỉ email của người liên hệ sẽ xuất hiện. Khi người dùng nhấn vào một trong các địa chỉ, ứng dụng email sẽ hiển thị màn hình để soạn thư địa chỉ email đã chọn.

Chế độ xem QuickContactBadge cung cấp quyền truy cập tức thì vào thông tin chi tiết của một người liên hệ và là cách nhanh chóng để liên lạc với người liên hệ đó. Người dùng không phải tra cứu một người liên hệ, tìm và sao chép thông tin, sau đó dán thông tin đó vào ứng dụng thích hợp. Thay vào đó, họ có thể nhấn vào QuickContactBadge, chọn phương thức giao tiếp mà họ muốn sử dụng và gửi trực tiếp thông tin cho phương thức đó đến ứng dụng thích hợp.

Thêm chế độ xem QuickContactHuy hiệu

Để thêm QuickContactBadge, hãy chèn Phần tử <QuickContactBadge> trong bố cục của bạn, như trong ví dụ sau:

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

Truy xuất dữ liệu nhà cung cấp

Để cho thấy một người liên hệ trong QuickContactBadge, bạn cần có URI nội dung cho điểm tiếp xúc và Bitmap cho hình ảnh nhỏ. Bạn tạo cả URI nội dung và Bitmap từ các cột được truy xuất từ Nhà cung cấp danh bạ. Chỉ định các cột này như một phần của phép chiếu mà bạn sử dụng để tải dữ liệu vào Cursor của bạn.

Đối với Android 3.0 (API cấp 11) trở lên, hãy thêm các cột sau vào phép chiếu:

Đối với Android 2.3.3 (API cấp 10) trở xuống, hãy sử dụng các cột sau:

Các ví dụ trên trang này giả định rằng Cursor chứa những cột này và bất kỳ cột nào khác đã chọn đã tải cột. Để tìm hiểu cách truy xuất các cột trong Cursor, hãy xem phần Truy xuất danh sách người liên hệ.

Đặt URI liên hệ và hình thu nhỏ

Sau khi có các cột cần thiết, bạn có thể liên kết dữ liệu với QuickContactBadge.

Đặt URI của địa chỉ liên hệ

Để đặt URI nội dung cho người liên hệ, hãy gọi getLookupUri(id,lookupKey) thành hãy nhận CONTENT_LOOKUP_URI, sau đó gọi assignContactUri() để thiết lập liên hệ. Lệnh này được minh hoạ trong ví dụ sau:

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

Khi người dùng nhấn vào biểu tượng QuickContactBadge, địa chỉ liên hệ chi tiết sẽ xuất hiện trong hộp thoại.

Đặt hình thu nhỏ của ảnh

Việc đặt URI của địa chỉ liên hệ cho QuickContactBadge không tự động tải ảnh thu nhỏ của địa chỉ liên hệ. Để tải ảnh, hãy lấy URI cho ảnh từ hàng Cursor của địa chỉ liên hệ, hãy sử dụng hàng này để mở tệp chứa ảnh thu nhỏ rồi đọc tệp trong Bitmap.

Lưu ý: Phần Không có cột PHOTO_THUMBNAIL_URI trong các phiên bản nền tảng trước 3.0. Đối với các phiên bản đó, bạn phải truy xuất URI từ bảng phụ Contacts.Photo.

Trước tiên, hãy thiết lập các biến để truy cập vào Cursor chứa các cột Contacts._IDContacts.LOOKUP_KEY:

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

Xác định một phương thức lấy dữ liệu liên quan đến ảnh cho người liên hệ và kích thước cho chế độ xem đích và trả về hình thu nhỏ có kích thước phù hợp trong Bitmap. Bắt đầu bằng cách tạo URI trỏ đến hình thu nhỏ:

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

Gọi phương thức loadContactPhotoThumbnail() trong mã của bạn để lấy giá trị hình thu nhỏ Bitmap, rồi sử dụng kết quả đó để đặt hình thu nhỏ của ảnh QuickContactBadge của bạn:

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

Thêm QuickContactBadge vào ListView

QuickContactBadge là một thành phần bổ sung hữu ích cho ListView hiển thị danh sách liên hệ. Sử dụng QuickContactBadge để hiển thị hình thu nhỏ của mỗi người liên hệ; khi người dùng nhấn vào hình thu nhỏ, hộp thoại QuickContactBadge sẽ xuất hiện.

Thêm phần tử QuickContactBadge

Để bắt đầu, hãy thêm phần tử thành phần hiển thị QuickContactBadge vào bố cục mục của bạn. Ví dụ: nếu bạn muốn hiển thị QuickContactBadge và tên cho từng người liên hệ mà bạn truy xuất, hãy đặt XML sau vào tệp bố cục:

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

Trong các phần sau, tệp này được gọi là contact_item_layout.xml.

Thiết lập CursorAdapter tuỳ chỉnh

Cách liên kết CursorAdapter với ListView chứa QuickContactBadge, hãy xác định một bộ chuyển đổi tuỳ chỉnh mở rộng CursorAdapter. Phương pháp này cho phép bạn xử lý dữ liệu trong Cursor trước khi liên kết dữ liệu đó với QuickContactBadge. Phương pháp này cũng cho phép bạn liên kết nhiều cột Cursor với QuickContactBadge. Không thể thực hiện bất kỳ thao tác nào trong số này trong CursorAdapter thông thường.

Lớp con của CursorAdapter mà bạn xác định phải ghi đè các phương thức sau:

CursorAdapter.newView()
Tăng cường một đối tượng View mới để giữ bố cục mục. Trong chế độ ghi đè của phương thức này, lưu trữ các tên người dùng thuộc đối tượng con View của bố cục, bao gồm cả phần tử con QuickContactBadge. Bằng cách sử dụng phương pháp này, bạn sẽ không phải lấy tay cầm cho các đối tượng View con mỗi khi tăng cường bố cục mới.

Bạn phải ghi đè phương thức này để có thể lấy tay cầm cho từng đối tượng View con. Kỹ thuật này cho phép bạn kiểm soát liên kết của chúng trong CursorAdapter.bindView().

CursorAdapter.bindView()
Di chuyển dữ liệu từ hàng Cursor hiện tại sang các đối tượng View con của bố cục mục. Bạn phải ghi đè phương thức này để có thể liên kết cả URI và hình thu nhỏ của người liên hệ với QuickContactBadge. Cách triển khai mặc định chỉ cho phép giao dịch một với một liên kết giữa cột và View.

Đoạn mã sau đây chứa ví dụ về một lớp con tuỳ chỉnh của CursorAdapter:

Xác định bộ chuyển đổi danh sách tuỳ chỉnh

Xác định lớp con của CursorAdapter! bao gồm hàm khởi tạo và ghi đè 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);
    }

Thiết lập biến

Trong mã, hãy thiết lập các biến bao gồm một phép chiếu Cursor chứa các cột cần thiết, như trong ví dụ sau.

Lưu ý: Các đoạn mã sau đây sử dụng phương thức loadContactPhotoThumbnail(), được xác định trong Phần Thiết lập URI của địa chỉ liên hệ và hình thu nhỏ.

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

Thiết lập ListView

Trong Fragment.onCreate(), hãy tạo thực thể tuỳ chỉnh bộ chuyển đổi con trỏ và xử lý 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);
        }
        ...
    }
    ...

Trong onViewCreated(), hãy liên kết ContactsAdapter với ListView:

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

Khi bạn nhận lại Cursor chứa dữ liệu danh bạ, thường là trong onLoadFinished(), hãy gọi swapCursor() để di chuyển dữ liệu Cursor sang ListView. Thao tác này sẽ hiển thị QuickContactBadge cho từng mục trong danh sách liên hệ.

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

Khi bạn liên kết Cursor với một ListView bằng CursorAdapter (hoặc lớp con) và bạn sử dụng CursorLoader để tải Cursor, luôn tham chiếu rõ ràng đến Cursor trong việc triển khai onLoaderReset(). Lệnh này được minh hoạ trong ví dụ sau:

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