عرض شارة الاتصال السريع

توضّح لك هذه الصفحة كيفية إضافة 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 (المستوى 11 لواجهة برمجة التطبيقات) والإصدارات الأحدث، عليك تضمين الأعمدة التالية في الإسقاط:

بالنسبة إلى الإصدار Android 2.3.3 (المستوى 10 لواجهة برمجة التطبيقات) والإصدارات الأقدم، استخدِم الأعمدة التالية:

تفترض الأمثلة في هذه الصفحة أنّه تم تحميل Cursor يحتوي على هذه الأعمدة وأي أعمدة أخرى محدّدة. للتعرّف على كيفية استرداد الأعمدة في Cursor، يمكنك الاطّلاع على استرداد قائمة جهات الاتصال.

ضبط معرّف الموارد المنتظم (URI) والصورة المصغّرة لجهة الاتصال

بعد توفّر الأعمدة اللازمة، يمكنك ربط البيانات بالسمة QuickContactBadge.

ضبط معرّف الموارد المنتظم (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، تظهر تفاصيل جهة الاتصال في مربّع الحوار.

ضبط الصورة المصغّرة للصورة

لا يؤدي ضبط معرّف موارد منتظم (URI) لجهة الاتصال QuickContactBadge إلى تحميل الصورة المصغّرة لجهة الاتصال تلقائيًا. لتحميل الصورة، احصل على معرّف موارد منتظم (URI) للصورة من صف Cursor لجهة الاتصال واستخدِمه لفتح الملف الذي يحتوي على الصورة المصغّرة المضغوطة، ثم اقرأ الملف في Bitmap.

ملاحظة: لا يتوفّر عمود PHOTO_THUMBNAIL_URI في إصدارات النظام الأساسي التي تسبق الإصدار 3.0. وفي تلك الإصدارات، عليك استرداد معرّف الموارد المنتظم (URI) من الجدول الفرعي Contacts.Photo.

أولاً، عليك إعداد المتغيّرات للوصول إلى Cursor التي تتضمّن عمودَي Contacts._ID وContacts.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);
    ...

حدِّد طريقة تجمع البيانات ذات الصلة بالصور لجهة الاتصال والأبعاد لطريقة العرض الوجهة وتعرض الصورة المصغّرة ذات الحجم المناسب في 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 إلى عرض القائمة

وتُعدّ العلامة 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 بـ ListView الذي يحتوي على QuickContactBadge، حدِّد محوِّلاً مخصّصًا يمتد إلى CursorAdapter. يتيح لك هذا الأسلوب معالجة البيانات في Cursor قبل ربطها بعنصر QuickContactBadge. يتيح لك هذا الأسلوب أيضًا ربط عدة أعمدة Cursor بالعنصر QuickContactBadge. ولا يمكن إجراء أي من هذه العمليات من خلال CursorAdapter العادي.

يجب أن تلغي الفئة الفرعية CursorAdapter التي تحدّدها الطرق التالية:

CursorAdapter.newView()
تضخّم عنصر View جديد للاحتفاظ بتنسيق العنصر. في حال إلغاء هذه الطريقة، يتم تخزين الأسماء المعرّفة لكائنات View الثانوية في التنسيق، بما في ذلك العنصر QuickContactBadge الثانوي. ومن خلال اتّباع هذا الأسلوب، ستتجنّب الحاجة إلى إضافة الأسماء المعرِّفة إلى عناصر 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 الذي يتضمّن الأعمدة اللازمة، كما هو موضّح في المثال التالي.

ملاحظة: تستخدِم مقتطفات الرمز التالي الطريقة loadContactPhotoThumbnail()، المحدّدة في قسم ضبط معرّف الموارد المنتظم (URI) والصورة المصغّرة لجهة الاتصال.

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

إعداد عرض على شكل قائمة

في 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()، اربط ContactsAdapter بـ 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);
        }
        ...
    }
    ...

عند استرداد 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);
    }

عند ربط Cursor بالعنصر ListView باستخدام CursorAdapter (أو فئة فرعية)، واستخدام CursorLoader لتحميل Cursor، يجب دائمًا أن تكون هناك إشارات واضحة إلى Cursor في تنفيذ onLoaderReset(). يظهر ذلك في المثال التالي:

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