Eine Liste von Kontakten abrufen

In dieser Lektion erfahren Sie, wie Sie mit den folgenden Methoden eine Liste von Kontakten abrufen, deren Daten ganz oder teilweise mit einem Suchstring übereinstimmen:

Kontaktnamen abgleichen
Sie können eine Liste von Kontakten abrufen, indem Sie den Suchstring ganz oder teilweise mit den Daten zu den Kontaktdaten abgleichen. Der Contacts Provider lässt mehrere Instanzen mit demselben Namen zu, sodass dieses Verfahren eine Liste von Übereinstimmungen zurückgeben kann.
Übereinstimmungen mit einem bestimmten Datentyp, z. B. einer Telefonnummer, zuordnen
Rufen Sie eine Liste von Kontakten ab, indem Sie den Suchstring mit einem bestimmten Detaildatentyp wie einer E-Mail-Adresse abgleichen. Mit dieser Technik können Sie beispielsweise alle Kontakte auflisten, deren E-Mail-Adresse mit dem Suchstring übereinstimmt.
Jede Art von Daten abgleichen
Sie können eine Liste von Kontakten abrufen, indem Sie den Suchstring mit einem beliebigen Detailtyp wie Name, Telefonnummer, Adresse oder E-Mail-Adresse abgleichen. Mit dieser Technik können Sie beispielsweise jeden Datentyp für einen Suchstring akzeptieren und dann die Kontakte auflisten, bei denen die Daten mit dem String übereinstimmen.

Hinweis: In allen Beispielen in dieser Lektion wird ein CursorLoader verwendet, um Daten vom Contacts Provider abzurufen. Ein CursorLoader führt seine Abfrage in einem Thread aus, der vom UI-Thread getrennt ist. Dadurch wird sichergestellt, dass die Abfrage die UI-Antwortzeiten nicht verlangsamt und zu einer schlechten Nutzererfahrung führt. Weitere Informationen findest du im Android-Trainingskurs Daten im Hintergrund laden.

Berechtigung zum Lesen des Anbieters anfordern

Für eine Suche bei Contacts Provider muss Ihre App die Berechtigung READ_CONTACTS haben. Füge dazu deiner Manifestdatei dieses <uses-permission>-Element als untergeordnetes Element von <manifest> hinzu:

    <uses-permission android:name="android.permission.READ_CONTACTS" />

Kontakte anhand des Namens abgleichen und Ergebnisse auflisten

Mit dieser Technik wird versucht, einen Suchstring mit dem Namen oder von Kontakten in der Tabelle ContactsContract.Contacts des Kontaktanbieters abzugleichen. Normalerweise möchten Sie die Ergebnisse in einem ListView anzeigen lassen, damit der Nutzer aus den übereinstimmenden Kontakten auswählen kann.

ListView- und Elementlayouts definieren

Damit die Suchergebnisse in einem ListView-Element angezeigt werden, benötigen Sie eine Hauptlayoutdatei, die die gesamte UI einschließlich ListView definiert, sowie eine Element-Layoutdatei, die eine Zeile von ListView definiert. Sie könnten beispielsweise die Hauptlayoutdatei res/layout/contacts_list_view.xml mit dem folgenden XML-Code erstellen:

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@android:id/list"
          android:layout_width="match_parent"
          android:layout_height="match_parent"/>

In dieser XML-Datei wird das integrierte Android-ListView-Widget android:id/list verwendet.

Definieren Sie die Elementlayoutdatei contacts_list_item.xml mit dem folgenden XML:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@android:id/text1"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:clickable="true"/>

In dieser XML-Datei wird das integrierte Android-TextView-Widget android:text1 verwendet.

Hinweis:In dieser Lektion wird nicht die Benutzeroberfläche zum Abrufen eines Suchstrings vom Nutzer beschrieben, da Sie den String möglicherweise indirekt abrufen möchten. Du kannst dem Nutzer beispielsweise die Möglichkeit geben, nach Kontakten zu suchen, deren Name mit einem String in einer eingehenden SMS übereinstimmt.

Die beiden von dir geschriebenen Layoutdateien definieren eine Benutzeroberfläche, auf der ListView angezeigt wird. Im nächsten Schritt schreiben Sie Code, der über diese Benutzeroberfläche eine Liste von Kontakten anzeigt.

Definieren Sie ein Fragment, das die Liste der Kontakte anzeigt

Wenn du die Liste der Kontakte aufrufen möchtest, definiere zuerst ein Fragment, das von einem Activity geladen wird. Fragment ist flexibler, da Sie eine Fragment zum Anzeigen der Liste und eine zweite Fragment zum Anzeigen der Details für einen Kontakt verwenden können, den der Nutzer aus der Liste auswählt. So können Sie eine der in dieser Lektion vorgestellten Techniken mit einer Technik aus der Lektion Details für einen Kontakt abrufen kombinieren.

Informationen zum Verwenden eines oder mehrerer Fragment-Objekte aus einem Activity finden Sie im Trainingskurs Eine dynamische UI mit Fragmenten erstellen.

Damit Sie Abfragen für den Contacts Provider schreiben können, bietet das Android-Framework eine Vertragsklasse namens ContactsContract, die nützliche Konstanten und Methoden für den Zugriff auf den Anbieter definiert. Wenn Sie diese Klasse verwenden, müssen Sie keine eigenen Konstanten für Inhalts-URIs, Tabellennamen oder Spalten definieren. Fügen Sie die folgende Anweisung ein, um diese Klasse zu verwenden:

Kotlin

import android.provider.ContactsContract

Java

import android.provider.ContactsContract;

Da der Code zum Abrufen von Daten vom Anbieter CursorLoader verwendet, müssen Sie angeben, dass er die Ladeschnittstelle LoaderManager.LoaderCallbacks implementiert. Implementieren Sie außerdem die Adapterschnittstelle AdapterView.OnItemClickListener, um leichter zu ermitteln, welchen Kontakt der Nutzer aus der Liste der Suchergebnisse auswählt. Beispiele:

Kotlin

...
import android.support.v4.app.Fragment
import android.support.v4.app.LoaderManager
import android.widget.AdapterView
...
class ContactsFragment :
        Fragment(),
        LoaderManager.LoaderCallbacks<Cursor>,
        AdapterView.OnItemClickListener {

Java

...
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.widget.AdapterView;
...
public class ContactsFragment extends Fragment implements
        LoaderManager.LoaderCallbacks<Cursor>,
        AdapterView.OnItemClickListener {

Globale Variablen definieren

Definieren Sie globale Variablen, die in anderen Teilen des Codes verwendet werden:

Kotlin

...
/*
 * Defines an array that contains column names to move from
 * the Cursor to the ListView.
 */
@SuppressLint("InlinedApi")
private val FROM_COLUMNS: Array<String> = arrayOf(
        if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)) {
            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
        } else {
            ContactsContract.Contacts.DISPLAY_NAME
        }
)
/*
 * Defines an array that contains resource ids for the layout views
 * that get the Cursor column contents. The id is pre-defined in
 * the Android framework, so it is prefaced with "android.R.id"
 */
private val TO_IDS: IntArray = intArrayOf(android.R.id.text1)
...
class ContactsFragment :
        Fragment(),
        LoaderManager.LoaderCallbacks<Cursor>,
        AdapterView.OnItemClickListener {
    ...
    // Define global mutable variables
    // Define a ListView object
    lateinit var contactsList: ListView
    // Define variables for the contact the user selects
    // The contact's _ID value
    var contactId: Long = 0
    // The contact's LOOKUP_KEY
    var contactKey: String? = null
    // A content URI for the selected contact
    var contactUri: Uri? = null
    // An adapter that binds the result Cursor to the ListView
    private val cursorAdapter: SimpleCursorAdapter? = null

Java

    ...
    /*
     * Defines an array that contains column names to move from
     * the Cursor to the ListView.
     */
    @SuppressLint("InlinedApi")
    private final static String[] FROM_COLUMNS = {
            Build.VERSION.SDK_INT
                    >= Build.VERSION_CODES.HONEYCOMB ?
                    ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
                    ContactsContract.Contacts.DISPLAY_NAME
    };
    /*
     * Defines an array that contains resource ids for the layout views
     * that get the Cursor column contents. The id is pre-defined in
     * the Android framework, so it is prefaced with "android.R.id"
     */
    private final static int[] TO_IDS = {
           android.R.id.text1
    };
    // Define global mutable variables
    // Define a ListView object
    ListView contactsList;
    // Define variables for the contact the user selects
    // The contact's _ID value
    long contactId;
    // The contact's LOOKUP_KEY
    String contactKey;
    // A content URI for the selected contact
    Uri contactUri;
    // An adapter that binds the result Cursor to the ListView
    private SimpleCursorAdapter cursorAdapter;
    ...

Hinweis:Da für Contacts.DISPLAY_NAME_PRIMARY Android 3.0 (API-Version 11) oder höher erforderlich ist, wird eine Android Lint-Warnung in Android Studio generiert, wenn du minSdkVersion auf 10 oder niedriger setzt. Wenn Sie diese Warnung deaktivieren möchten, fügen Sie die Annotation @SuppressLint("InlinedApi") vor der Definition von FROM_COLUMNS ein.

Fragment initialisieren

Initialisieren Sie Fragment. Fügen Sie den vom Android-System benötigten leeren, öffentlichen Konstruktor hinzu und blähen Sie die Benutzeroberfläche des Fragment-Objekts in der Callback-Methode onCreateView() auf. Beispiele:

Kotlin

    // A UI Fragment must inflate its View
    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        // Inflate the fragment layout
        return inflater.inflate(R.layout.contact_list_fragment, container, false)
    }

Java

    // Empty public constructor, required by the system
    public ContactsFragment() {}

    // A UI Fragment must inflate its View
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // Inflate the fragment layout
        return inflater.inflate(R.layout.contact_list_fragment,
            container, false);
    }

CursorAdapter für ListView einrichten

Richten Sie das SimpleCursorAdapter ein, mit dem die Ergebnisse der Suche an das ListView-Objekt gebunden werden. Um das ListView-Objekt abzurufen, das die Kontakte anzeigt, müssen Sie Activity.findViewById() mit der übergeordneten Aktivität von Fragment aufrufen. Verwende die Context der übergeordneten Aktivität, wenn du setAdapter() aufrufst. Beispiele:

Kotlin

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        ...
        // Gets the ListView from the View list of the parent activity
        activity?.also {
            contactsList = it.findViewById<ListView>(R.id.contact_list_view)
            // Gets a CursorAdapter
            cursorAdapter = SimpleCursorAdapter(
                    it,
                    R.layout.contact_list_item,
                    null,
                    FROM_COLUMNS, TO_IDS,
                    0
            )
            // Sets the adapter for the ListView
            contactsList.adapter = cursorAdapter
        }
    }

Java

    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ...
        // Gets the ListView from the View list of the parent activity
        contactsList =
            (ListView) getActivity().findViewById(R.layout.contact_list_view);
        // Gets a CursorAdapter
        cursorAdapter = new SimpleCursorAdapter(
                getActivity(),
                R.layout.contact_list_item,
                null,
                FROM_COLUMNS, TO_IDS,
                0);
        // Sets the adapter for the ListView
        contactsList.setAdapter(cursorAdapter);
    }

Ausgewählten Kontakt-Listener festlegen

Wenn die Ergebnisse einer Suche angezeigt werden, sollte der Nutzer in der Regel einen einzelnen Kontakt zur weiteren Verarbeitung auswählen können. Wenn der Nutzer beispielsweise auf einen Kontakt klickt, können Sie die Adresse des Kontakts auf einer Karte anzeigen lassen. Um diese Funktion bereitzustellen, haben Sie zuerst den aktuellen Fragment als Klick-Listener definiert. Dazu haben Sie angegeben, dass die Klasse AdapterView.OnItemClickListener implementiert, wie im Abschnitt Fragment definieren, das die Liste der Kontakte anzeigt beschrieben.

Binden Sie den Listener an die ListView, indem Sie die Methode setOnItemClickListener() in onActivityCreated() aufrufen, um mit der Einrichtung des Listeners fortzufahren. Beispiele:

Kotlin

    fun onActivityCreated(savedInstanceState:Bundle) {
        ...
        // Set the item click listener to be the current fragment.
        contactsList.onItemClickListener = this
        ...
    }

Java

    public void onActivityCreated(Bundle savedInstanceState) {
        ...
        // Set the item click listener to be the current fragment.
        contactsList.setOnItemClickListener(this);
        ...
    }

Da du angegeben hast, dass die aktuelle Fragment der OnItemClickListener für die ListView ist, musst du nun die erforderliche Methode onItemClick() implementieren, die das Klickereignis verarbeitet. Dies wird in einem nachfolgenden Abschnitt beschrieben.

Projektion definieren

Definieren Sie eine Konstante, die die Spalten enthält, die Sie von Ihrer Abfrage zurückgeben möchten. Für jedes Element im ListView wird der Anzeigename des Kontakts angezeigt, der die Hauptform des Kontaktnamens enthält. In Android 3.0 (API-Version 11) und höher lautet der Name dieser Spalte Contacts.DISPLAY_NAME_PRIMARY. In früheren Versionen lautet der Name dieser Spalte Contacts.DISPLAY_NAME.

Die Spalte Contacts._ID wird vom Bindungsprozess SimpleCursorAdapter verwendet. Contacts._ID und LOOKUP_KEY werden zusammen verwendet, um einen Inhalts-URI für den Kontakt zu erstellen, den der Nutzer ausgewählt hat.

Kotlin

...
@SuppressLint("InlinedApi")
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
)

Java

...
@SuppressLint("InlinedApi")
private static final String[] PROJECTION =
        {
            Contacts._ID,
            Contacts.LOOKUP_KEY,
            Build.VERSION.SDK_INT
                    >= Build.VERSION_CODES.HONEYCOMB ?
                    ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
                    ContactsContract.Contacts.DISPLAY_NAME

        };

Konstanten für die Cursor-Spaltenindexe definieren

Zum Abrufen von Daten aus einer einzelnen Spalte in einem Cursor benötigen Sie den Index der Spalte innerhalb von Cursor. Sie können Konstanten für die Indexe der Cursor-Spalten definieren, da die Indexe der Reihenfolge der Spaltennamen in Ihrer Projektion entsprechen. Beispiele:

Kotlin

// The column index for the _ID column
private const val CONTACT_ID_INDEX: Int = 0
// The column index for the CONTACT_KEY column
private const val CONTACT_KEY_INDEX: Int = 1

Java

// The column index for the _ID column
private static final int CONTACT_ID_INDEX = 0;
// The column index for the CONTACT_KEY column
private static final int CONTACT_KEY_INDEX = 1;

Auswahlkriterien festlegen

Um die gewünschten Daten anzugeben, erstellen Sie eine Kombination aus Textausdrücken und Variablen, die dem Anbieter mitteilen, welche Datenspalten gesucht werden sollen und welche Werte gesucht werden sollen.

Definieren Sie für den Textausdruck eine Konstante, die die Suchspalten auflistet. Obwohl dieser Ausdruck auch Werte enthalten kann, sollten die Werte am besten durch einen Platzhalter "?" dargestellt werden. Beim Abrufen wird der Platzhalter durch Werte aus einem Array ersetzt. Wenn Sie „?“ als Platzhalter verwenden, wird die Suchspezifikation durch Bindung und nicht durch SQL-Kompilierung generiert. Diese Vorgehensweise eliminiert die Möglichkeit einer schädlichen SQL-Einschleusung. Beispiele:

Kotlin

// Defines the text expression
@SuppressLint("InlinedApi")
private val SELECTION: String =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            "${ContactsContract.Contacts.DISPLAY_NAME_PRIMARY} LIKE ?"
        else
            "${ContactsContract.Contacts.DISPLAY_NAME} LIKE ?"
...
    // Defines a variable for the search string
    private val searchString: String = ...
    // Defines the array to hold values that replace the ?
    private val selectionArgs = arrayOf<String>(searchString)

Java

    // Defines the text expression
    @SuppressLint("InlinedApi")
    private static final String SELECTION =
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
            Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :
            Contacts.DISPLAY_NAME + " LIKE ?";
    // Defines a variable for the search string
    private String searchString;
    // Defines the array to hold values that replace the ?
    private String[] selectionArgs = { searchString };

onItemClick()-Methode definieren

In einem vorherigen Abschnitt haben Sie den Element-Klick-Listener für ListView festgelegt. Implementieren Sie nun die Aktion für den Listener, indem Sie die Methode AdapterView.OnItemClickListener.onItemClick() definieren:

Kotlin

    override fun onItemClick(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
        // Get the Cursor
        val cursor: Cursor? = (parent.adapter as? CursorAdapter)?.cursor?.apply {
            // Move to the selected contact
            moveToPosition(position)
            // Get the _ID value
            contactId = getLong(CONTACT_ID_INDEX)
            // Get the selected LOOKUP KEY
            contactKey = getString(CONTACT_KEY_INDEX)
            // Create the contact's content Uri
            contactUri = ContactsContract.Contacts.getLookupUri(contactId, mContactKey)
            /*
             * You can use contactUri as the content URI for retrieving
             * the details for a contact.
             */
        }
    }

Java

    @Override
    public void onItemClick(
        AdapterView<?> parent, View item, int position, long rowID) {
        // Get the Cursor
        Cursor cursor = parent.getAdapter().getCursor();
        // Move to the selected contact
        cursor.moveToPosition(position);
        // Get the _ID value
        contactId = cursor.getLong(CONTACT_ID_INDEX);
        // Get the selected LOOKUP KEY
        contactKey = cursor.getString(CONTACT_KEY_INDEX);
        // Create the contact's content Uri
        contactUri = Contacts.getLookupUri(contactId, mContactKey);
        /*
         * You can use contactUri as the content URI for retrieving
         * the details for a contact.
         */
    }

Loader initialisieren

Da Sie zum Abrufen von Daten eine CursorLoader verwenden, müssen Sie den Hintergrundthread und andere Variablen initialisieren, die den asynchronen Abruf steuern. Führe die Initialisierung in onCreate() durch, wie im folgenden Beispiel gezeigt:

Kotlin

class ContactsFragment :
        Fragment(),
        LoaderManager.LoaderCallbacks<Cursor> {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        // Always call the super method first
        super.onCreate(savedInstanceState)
        ...
        // Initializes the loader
        loaderManager.initLoader(0, null, this)

Java

public class ContactsFragment extends Fragment implements
        LoaderManager.LoaderCallbacks<Cursor> {
    ...
    // Called just before the Fragment displays its UI
    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Always call the super method first
        super.onCreate(savedInstanceState);
        ...
        // Initializes the loader
        getLoaderManager().initLoader(0, null, this);

onCreateLoader() implementieren

Implementieren Sie die Methode onCreateLoader(), die vom Lade-Framework sofort nach dem Aufruf von initLoader() aufgerufen wird.

Richten Sie das Suchstringmuster in onCreateLoader() ein. Um einen String in ein Muster umzuwandeln, fügen Sie „%“-Zeichen (Prozentzeichen) für eine Folge von null oder mehr Zeichen oder „_“ (Unterstrich) für ein einzelnes Zeichen oder beides ein. Das Muster „%Jefferson%“ würde beispielsweise sowohl mit „Thomas Jefferson“ als auch mit „Jefferson Davis“ übereinstimmen.

Gibt ein neues CursorLoader-Objekt von der Methode zurück. Verwenden Sie für den Inhalts-URI Contacts.CONTENT_URI. Dieser URI bezieht sich auf die gesamte Tabelle, wie im folgenden Beispiel gezeigt:

Kotlin

    ...
    override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
        /*
         * Makes search string into pattern and
         * stores it in the selection array
         */
        selectionArgs[0] = "%$mSearchString%"
        // Starts the query
        return activity?.let {
            return CursorLoader(
                    it,
                    ContactsContract.Contacts.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    selectionArgs,
                    null
            )
        } ?: throw IllegalStateException()
    }

Java

    ...
    @Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        /*
         * Makes search string into pattern and
         * stores it in the selection array
         */
        selectionArgs[0] = "%" + searchString + "%";
        // Starts the query
        return new CursorLoader(
                getActivity(),
                ContactsContract.Contacts.CONTENT_URI,
                PROJECTION,
                SELECTION,
                selectionArgs,
                null
        );
    }

onLoadFinished() und onLoaderReset() implementieren

Implementieren Sie die Methode onLoadFinished(). Das Lade-Framework ruft onLoadFinished() auf, wenn der Contacts Provider die Ergebnisse der Abfrage zurückgibt. Setzen Sie bei dieser Methode das Ergebnis Cursor in SimpleCursorAdapter. Dadurch wird ListView automatisch mit den Suchergebnissen aktualisiert:

Kotlin

    override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
        // Put the result Cursor in the adapter for the ListView
        cursorAdapter?.swapCursor(cursor)
    }

Java

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        // Put the result Cursor in the adapter for the ListView
        cursorAdapter.swapCursor(cursor);
    }

Die Methode onLoaderReset() wird aufgerufen, wenn das Lade-Framework erkennt, dass das Ergebnis Cursor veraltete Daten enthält. Löschen Sie den SimpleCursorAdapter-Verweis auf den vorhandenen Cursor. Andernfalls wird Cursor vom Lade-Framework nicht wiederverwendet, was zu einem Speicherleck führt. Beispiele:

Kotlin

    override fun onLoaderReset(loader: Loader<Cursor>) {
        // Delete the reference to the existing Cursor
        cursorAdapter?.swapCursor(null)
    }

Java

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // Delete the reference to the existing Cursor
        cursorAdapter.swapCursor(null);

    }

Sie haben nun die wichtigsten Bestandteile einer Anwendung, die einen Suchstring mit Kontaktnamen abstimmt und das Ergebnis in einem ListView zurückgibt. Der Nutzer kann auf den Namen eines Kontakts klicken, um ihn auszuwählen. Dies löst einen Listener aus, in dem Sie weiter mit den Kontaktdaten arbeiten können. Sie können beispielsweise die Details des Kontakts abrufen. Wie das geht, erfahren Sie in der nächsten Lektion: Details für einen Kontakt abrufen.

Weitere Informationen zu Suchoberflächen finden Sie im API-Leitfaden Suchoberfläche erstellen.

In den verbleibenden Abschnitten dieser Lektion werden andere Möglichkeiten zum Auffinden von Kontakten im Contacts Provider beschrieben.

Kontakte anhand eines bestimmten Datentyps abgleichen

Mit dieser Technik können Sie den Datentyp angeben, der abgeglichen werden soll. Das Abrufen nach Namen ist ein spezifisches Beispiel für diesen Abfragetyp. Sie können dies aber auch für alle Arten von Detaildaten tun, die mit einem Kontakt verknüpft sind. Sie können beispielsweise Kontakte mit einer bestimmten Postleitzahl abrufen. In diesem Fall muss der Suchstring mit den Daten übereinstimmen, die in einer Postleitzahlzeile gespeichert sind.

Implementieren Sie zuerst den folgenden Code, wie in den vorherigen Abschnitten beschrieben, um diese Art von Abruf zu implementieren:

  • Fordern Sie eine Leseberechtigung für den Dienstanbieter an.
  • Definieren Sie ListView- und Elementlayouts.
  • Definieren Sie ein Fragment, das die Liste der Kontakte anzeigt.
  • Globale Variablen definieren
  • Initialisieren Sie das Fragment.
  • Richten Sie den CursorAdapter für die ListView ein.
  • Legt den ausgewählten Kontakt-Listener fest.
  • Definieren Sie Konstanten für die Cursor-Spaltenindexe.

    Obwohl Sie Daten aus einer anderen Tabelle abrufen, ist die Reihenfolge der Spalten in der Projektion gleich, sodass Sie dieselben Indexe für den Cursor verwenden können.

  • Definieren Sie die onItemClick()-Methode.
  • Initialisieren Sie das Ladeprogramm.
  • Implementieren Sie onLoadFinished() und onLoaderReset().

In den folgenden Schritten wird der zusätzliche Code beschrieben, den Sie benötigen, um einen Suchstring mit einem bestimmten Detaildatentyp abzugleichen und die Ergebnisse anzuzeigen.

Datentyp und Tabelle auswählen

Wenn Sie nach einem bestimmten Detaildatentyp suchen möchten, müssen Sie den benutzerdefinierten MIME-Typ-Wert für den Datentyp kennen. Jeder Datentyp hat einen eindeutigen MIME-Typ-Wert, der durch eine Konstante CONTENT_ITEM_TYPE in der abgeleiteten Klasse von ContactsContract.CommonDataKinds definiert wird, die dem Datentyp zugeordnet ist. Die Namen der abgeleiteten Klassen haben Namen, die ihren Datentyp angeben. Die abgeleitete Klasse für E-Mail-Daten ist beispielsweise ContactsContract.CommonDataKinds.Email und der benutzerdefinierte MIME-Typ für E-Mail-Daten wird durch die Konstante Email.CONTENT_ITEM_TYPE definiert.

Verwenden Sie die Tabelle ContactsContract.Data für Ihre Suche. Alle Konstanten, die Sie für die Projektion, Auswahlklausel und Sortierreihenfolge benötigen, werden in dieser Tabelle definiert oder von ihr übernommen.

Projektion definieren

Wenn Sie eine Projektion definieren möchten, wählen Sie eine oder mehrere der in ContactsContract.Data definierten Spalten oder die Klassen aus, von denen die Projektion übernommen wird. Der Contacts Provider führt einen impliziten Join zwischen ContactsContract.Data und anderen Tabellen durch, bevor er Zeilen zurückgibt. Beispiele:

Kotlin

@SuppressLint("InlinedApi")
private val PROJECTION: Array<out String> = arrayOf(
        /*
         * The detail data row ID. To make a ListView work,
         * this column is required.
         */
        ContactsContract.Data._ID,
        // The primary display name
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            ContactsContract.Data.DISPLAY_NAME_PRIMARY
        else
            ContactsContract.Data.DISPLAY_NAME,
        // The contact's _ID, to construct a content URI
        ContactsContract.Data.CONTACT_ID,
        // The contact's LOOKUP_KEY, to construct a content URI
        ContactsContract.Data.LOOKUP_KEY
)

Java

    @SuppressLint("InlinedApi")
    private static final String[] PROJECTION =
        {
            /*
             * The detail data row ID. To make a ListView work,
             * this column is required.
             */
            ContactsContract.Data._ID,
            // The primary display name
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
                    ContactsContract.Data.DISPLAY_NAME_PRIMARY :
                    ContactsContract.Data.DISPLAY_NAME,
            // The contact's _ID, to construct a content URI
            ContactsContract.Data.CONTACT_ID,
            // The contact's LOOKUP_KEY, to construct a content URI
            ContactsContract.Data.LOOKUP_KEY // A permanent link to the contact
        };

Suchkriterien definieren

Um in einem bestimmten Datentyp nach einem String zu suchen, erstellen Sie eine Auswahlklausel aus folgenden Elementen:

  • Der Name der Spalte, die den Suchstring enthält. Dieser Name variiert je nach Datentyp. Daher müssen Sie die abgeleitete Klasse von ContactsContract.CommonDataKinds ermitteln, die dem Datentyp entspricht, und dann den Spaltennamen aus dieser abgeleiteten Klasse auswählen. Verwenden Sie beispielsweise die Spalte Email.ADDRESS, um nach E-Mail-Adressen zu suchen.
  • Der Suchstring selbst, dargestellt in der Auswahlklausel als „?“-Zeichen.
  • Der Name der Spalte, die den benutzerdefinierten MIME-Typ-Wert enthält. Dieser Name lautet immer Data.MIMETYPE.
  • Der benutzerdefinierte MIME-Typ-Wert für den Datentyp. Wie bereits beschrieben, ist dies die Konstante CONTENT_ITEM_TYPE in der abgeleiteten Klasse ContactsContract.CommonDataKinds. Der MIME-Typ-Wert für E-Mail-Daten ist beispielsweise Email.CONTENT_ITEM_TYPE. Setzen Sie den Wert in einfache Anführungszeichen, indem Sie ein '-Zeichen (einfaches Anführungszeichen) mit dem Anfang und Ende der Konstante verketten. Andernfalls interpretiert der Anbieter den Wert als Variablennamen und nicht als Stringwert. Für diesen Wert müssen Sie keinen Platzhalter verwenden, da es sich hierbei um eine Konstante und nicht um einen vom Nutzer bereitgestellten Wert.

Beispiele:

Kotlin

/*
 * Constructs search criteria from the search string
 * and email MIME type
 */
private val SELECTION: String =
        /*
         * Searches for an email address
         * that matches the search string
         */
        "${Email.ADDRESS} LIKE ? AND " +
        /*
         * Searches for a MIME type that matches
         * the value of the constant
         * Email.CONTENT_ITEM_TYPE. Note the
         * single quotes surrounding Email.CONTENT_ITEM_TYPE.
         */
        "${ContactsContract.Data.MIMETYPE } = '${Email.CONTENT_ITEM_TYPE}'"

Java

    /*
     * Constructs search criteria from the search string
     * and email MIME type
     */
    private static final String SELECTION =
            /*
             * Searches for an email address
             * that matches the search string
             */
            Email.ADDRESS + " LIKE ? " + "AND " +
            /*
             * Searches for a MIME type that matches
             * the value of the constant
             * Email.CONTENT_ITEM_TYPE. Note the
             * single quotes surrounding Email.CONTENT_ITEM_TYPE.
             */
            ContactsContract.Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";

Definieren Sie als Nächstes Variablen, die das Auswahlargument enthalten:

Kotlin

    private var searchString: String? = null
    private val selectionArgs: Array<String> = arrayOf("")

Java

    String searchString;
    String[] selectionArgs = { "" };

onCreateLoader() implementieren

Nachdem Sie nun die gewünschten Daten angegeben haben und ermitteln, wie Sie diese Daten finden, definieren Sie eine Abfrage in Ihrer Implementierung von onCreateLoader(). Über diese Methode wird eine neue CursorLoader zurückgegeben. Verwenden Sie dabei die Projektion, den Auswahltextausdruck und das Auswahlarray als Argumente. Verwenden Sie Data.CONTENT_URI als Inhalts-URI. Beispiele:

Kotlin

    override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
        // OPTIONAL: Makes search string into pattern
        searchString = "%$mSearchString%"

        searchString?.also {
            // Puts the search string into the selection criteria
            selectionArgs[0] = it
        }
        // Starts the query
        return activity?.let {
            CursorLoader(
                    it,
                    ContactsContract.Data.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    selectionArgs,
                    null
            )
        } ?: throw IllegalStateException()
    }

Java

@Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        // OPTIONAL: Makes search string into pattern
        searchString = "%" + searchString + "%";
        // Puts the search string into the selection criteria
        selectionArgs[0] = searchString;
        // Starts the query
        return new CursorLoader(
                getActivity(),
                Data.CONTENT_URI,
                PROJECTION,
                SELECTION,
                selectionArgs,
                null
        );
    }

Diese Code-Snippets sind die Grundlage für einen einfachen umgekehrten Lookup, der auf einer bestimmten Art von Detaildaten basiert. Dies ist die beste Methode, wenn Ihre Anwendung auf einen bestimmten Datentyp wie E-Mails konzentriert ist und Sie Nutzern ermöglichen möchten, die mit einem Datenelement verknüpften Namen abzurufen.

Kontakte anhand beliebiger Datentypen abgleichen

Wenn Sie einen Kontakt auf Grundlage beliebiger Datentypen abrufen, werden Kontakte zurückgegeben, wenn eines der Daten mit dem Suchstring übereinstimmt, z. B. Name, E-Mail-Adresse, Postanschrift oder Telefonnummer. Dadurch erhalten Sie ein breites Spektrum an Suchergebnissen. Wenn der Suchstring beispielsweise „Mustermann“ lautet, wird bei der Suche nach einem beliebigen Datentyp der Kontakt „Max Mustermann“ zurückgegeben. Es werden auch Kontakte zurückgegeben, die in der Hausstraße wohnen.

Implementieren Sie zuerst den folgenden Code, wie in den vorherigen Abschnitten beschrieben, um diese Art von Abruf zu implementieren:

  • Fordern Sie eine Leseberechtigung für den Dienstanbieter an.
  • Definieren Sie ListView- und Elementlayouts.
  • Definieren Sie ein Fragment, das die Liste der Kontakte anzeigt.
  • Globale Variablen definieren
  • Initialisieren Sie das Fragment.
  • Richten Sie den CursorAdapter für die ListView ein.
  • Legt den ausgewählten Kontakt-Listener fest.
  • Definieren Sie eine Projektion.
  • Definieren Sie Konstanten für die Cursor-Spaltenindexe.

    Für diese Art des Abrufs verwenden Sie dieselbe Tabelle, die Sie im Abschnitt Kontakt nach Name abgleichen und Ergebnisse auflisten verwendet haben. Verwenden Sie auch die gleichen Spaltenindexe.

  • Definieren Sie die onItemClick()-Methode.
  • Initialisieren Sie das Ladeprogramm.
  • Implementieren Sie onLoadFinished() und onLoaderReset().

In den folgenden Schritten wird der zusätzliche Code beschrieben, den Sie benötigen, um einen Suchstring mit allen Datentypen abzugleichen und die Ergebnisse anzuzeigen.

Auswahlkriterien entfernen

Definieren Sie nicht die SELECTION-Konstanten oder die mSelectionArgs-Variable. Sie werden bei dieser Art des Abrufs nicht verwendet.

onCreateLoader() implementieren

Implementieren Sie die Methode onCreateLoader() und geben Sie einen neuen CursorLoader-Wert zurück. Sie müssen den Suchstring nicht in ein Muster konvertieren, da der Contacts Provider dies automatisch tut. Verwenden Sie Contacts.CONTENT_FILTER_URI als Basis-URI und hängen Sie den Suchstring an diesen an, indem Sie Uri.withAppendedPath() aufrufen. Die Verwendung dieses URI löst automatisch die Suche nach einem beliebigen Datentyp aus, wie im folgenden Beispiel gezeigt:

Kotlin

    override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
        /*
         * Appends the search string to the base URI. Always
         * encode search strings to ensure they're in proper
         * format.
         */
        val contentUri: Uri = Uri.withAppendedPath(
                ContactsContract.Contacts.CONTENT_FILTER_URI,
                Uri.encode(searchString)
        )
        // Starts the query
        return activity?.let {
            CursorLoader(
                    it,
                    contentUri,
                    PROJECTION2,
                    null,
                    null,
                    null
            )
        } ?: throw IllegalStateException()
    }

Java

    @Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        /*
         * Appends the search string to the base URI. Always
         * encode search strings to ensure they're in proper
         * format.
         */
        Uri contentUri = Uri.withAppendedPath(
                Contacts.CONTENT_FILTER_URI,
                Uri.encode(searchString));
        // Starts the query
        return new CursorLoader(
                getActivity(),
                contentUri,
                PROJECTION,
                null,
                null,
                null
        );
    }

Diese Code-Snippets sind die Grundlage einer App, die eine umfassende Suche nach dem Contacts Provider durchführt. Das Verfahren eignet sich für Apps, in denen eine Funktion implementiert werden soll, die der App „Kontakte“ entspricht.