Отображение значка быстрого контакта

На этой странице показано, как добавить 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() , чтобы установить контакт. Это показано в следующем примере:

    // 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 , в диалоговом окне появляются сведения о контакте.

Установить миниатюру фотографии

При установке URI контакта для QuickContactBadge миниатюра фотографии контакта не загружается автоматически. Чтобы загрузить фотографию, получите URI для фотографии из строки Cursor контакта, используйте его, чтобы открыть файл, содержащий сжатую миниатюру фотографии, и считайте файл в Bitmap .

Примечание. Столбец PHOTO_THUMBNAIL_URI недоступен в версиях платформы до 3.0. Для этих версий необходимо получить URI из подтаблицы Contacts.Photo .

Сначала настройте переменные для доступа к Cursor , содержащему столбцы Contacts._ID и Contacts.LOOKUP_KEY :

    // 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, указывающего на миниатюру:

    /**
     * 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 :

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

Добавьте QuickContactBadge в ListView

QuickContactBadge — это полезное дополнение к ListView , отображающее список контактов. Используйте QuickContactBadge для отображения миниатюры фотографий для каждого контакта; когда пользователи касаются миниатюры, появляется диалоговое окно QuickContactBadge .

Добавьте элемент QuickContactBadge.

Для начала добавьте элемент представления QuickContactBadge в макет элемента. Например, если вы хотите отображать QuickContactBadge и имя для каждого полученного контакта, поместите следующий XML-код в файл макета:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
               
android:layout_width="match_parent"
               
android:layout_height="wrap_content">
   
<QuickContactBadge
       
android:id="@+id/quickcontact"
       
android:layout_height="wrap_content"
       
android:layout_width="wrap_content"
       
android:scaleType="centerCrop"/>
   
<TextView android:id="@+id/displayname"
             
android:layout_width="match_parent"
             
android:layout_height="wrap_content"
             
android:layout_toRightOf="@+id/quickcontact"
             
android:gravity="center_vertical"
             
android:layout_alignParentRight="true"
             
android:layout_alignParentTop="true"/>
</RelativeLayout>

В следующих разделах этот файл называется contact_item_layout.xml .

Настройка пользовательского CursorAdapter

Чтобы привязать 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() :

    /**
     * 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 , включающую необходимые столбцы, как показано в следующем примере.

Примечание. В следующих фрагментах кода используется метод loadContactPhotoThumbnail() , который определен в разделе «Установка URI контакта и эскиза» .

/*
 * 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 :

    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() привяжите ContactsAdapter к ListView :

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 .

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

Когда вы привязываете Cursor к ListView с помощью CursorAdapter (или подкласса) и используете CursorLoader для загрузки Cursor , всегда удаляйте ссылки на Cursor в вашей реализации onLoaderReset() . Это показано в следующем примере:

    override fun onLoaderReset(loader: Loader<Cursor>) {
       
// Removes remaining reference to the previous Cursor
        adapter
?.swapCursor(null)
   
}
    @Override
   
public void onLoaderReset(Loader<Cursor> loader) {
       
// Removes remaining reference to the previous Cursor
        adapter
.swapCursor(null);
   
}