Pobieranie listy kontaktów

Z tej lekcji dowiesz się, jak pobrać listę kontaktów, których dane pasują do całości lub części ciągu znaków w wyszukiwaniu, korzystając z tych metod:

Dopasuj nazwy kontaktów
Pobierz listę kontaktów, dopasowując ciąg wyszukiwania do całości lub części kontaktu nazwy. Dostawca kontaktów zezwala na występowanie wielu wystąpień tej samej nazwy, więc ta technika może zwrócić listę dopasowań.
Dopasowanie do określonego typu danych, np. numeru telefonu
Pobierz listę kontaktów, dopasowując ciąg wyszukiwania do określonego typu szczegółów takie jak adres e-mail. Dzięki temu możesz na przykład wyświetlić listę wszystkich kontaktów, których adresy e-mail pasują do ciągu wyszukiwania.
Dopasuj dowolny typ danych
Pobierz listę kontaktów, dopasowując ciąg wyszukiwania do dowolnego typu danych szczegółowych. w tym imię i nazwisko, numer telefonu, adres pocztowy, adres e-mail itp. Przykład: Ta metoda pozwala akceptować dowolny typ danych dla wyszukiwanego ciągu, a następnie wyświetlać kontakty, których dane pasują do ciągu.

Uwaga: we wszystkich przykładach w tej lekcji używany jest atrybut CursorLoader, aby pobrać dane z Kontaktów Dostawca. CursorLoader wykonuje zapytanie w wątku, który jest oddzielony od wątku interfejsu. Dzięki temu zapytanie nie spowalnia interfejsu użytkownika czas reakcji i pogarsza wrażenia użytkowników. Więcej informacji znajdziesz w sesji szkoleniowej na temat Androida Załadowywanie danych w tle.

Prośba o dostęp do odczytu dostawcy

Aby można było przeszukać dostawcę kontaktów, aplikacja musi mieć Uprawnienie READ_CONTACTS. Aby wysłać prośbę, dodaj ten element <uses-permission> do pliku manifestu jako element potomny <manifest>:

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

Dopasuj kontakt według nazwy i wymień wyniki

Ta metoda próbuje dopasować wyszukiwany ciąg do nazwy kontaktu lub kontaktów w Skontaktuj się z tabelą ContactsContract.Contacts dostawcy. Najczęściej aby wyświetlić wyniki w polu ListView, umożliwiając użytkownikowi wybranie pasujących kontaktów.

Definiowanie układów ListView i elementów

Aby wyświetlać wyniki wyszukiwania w ListView, potrzebujesz głównego pliku układu, który definiuje cały interfejs użytkownika, w tym ListView, oraz pliku układu elementu, który definiuje jeden wiersz ListView. Możesz na przykład utworzyć główny plik układu res/layout/contacts_list_view.xml za pomocą tego kodu XML:

<?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"/>

Ten kod XML używa wbudowanego widżetu ListView na Androida. android:id/list

Określ plik układu contacts_list_item.xml za pomocą tego kodu 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"/>

Ten plik XML wykorzystuje wbudowany widżet Androida TextView. android:text1

Uwaga: ten samouczek nie opisuje interfejsu służącego do pobierania ciągu wyszukiwania od użytkownika, ponieważ możesz chcieć pobrać ten ciąg pośrednio. Możesz na przykład umożliwić użytkownikowi wyszukiwanie kontaktów, których nazwa pasuje do ciągu znaków w przychodzącej wiadomości tekstowej.

Oba utworzone przez Ciebie pliki układu definiują interfejs użytkownika, który wyświetlaListView. Kolejnym krokiem jest napisanie kodu, który używa tego interfejsu do wyświetlania listy kontaktów.

Zdefiniuj fragment wyświetlający listę kontaktów

Aby wyświetlić listę kontaktów, zacznij od zdefiniowania Fragment wczytywane przez: Activity. Za pomocą Fragment to bardziej elastyczna technika, ponieważ można stosować jeden Fragment, aby wyświetlić listę, oraz drugi Fragment, aby wyświetlić szczegóły kontaktu wybierze produkt z listy. Stosując tę metodę, możesz połączyć jedną z technik przedstawionych na tę lekcję w ramach lekcji Pobieranie szczegółów kontaktu

Aby dowiedzieć się, jak używać co najmniej jednego obiektu Fragment z poziomu Activity, przeczytaj zajęcia szkoleniowe Tworzenie dynamicznego interfejsu użytkownika za pomocą fragmentów.

Aby ułatwić pisanie zapytań dotyczących dostawcy kontaktów, platforma Androida udostępnia klasy kontraktów o nazwie ContactsContract, która definiuje użyteczność i metody uzyskiwania dostępu do dostawcy. Korzystając z tej klasy, nie musisz definiować własnych stałych dla URI treści, nazw tabel ani kolumn. Aby korzystać z tej klasy, dodaj to oświadczenie:

Kotlin

import android.provider.ContactsContract

Java

import android.provider.ContactsContract;

Ponieważ kod pobiera dane za pomocą parametru CursorLoader od dostawcy, musisz wskazać, że implementuje interfejs wczytywania LoaderManager.LoaderCallbacks Aby ułatwić wykrywanie kontaktów, użytkownik wybiera z listy wyników wyszukiwania, zaimplementuj interfejs adaptera AdapterView.OnItemClickListener Na przykład:

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 {

Definiowanie zmiennych globalnych

Zdefiniuj zmienne globalne, które są używane w innych częściach kodu:

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

Uwaga: ponieważ Contacts.DISPLAY_NAME_PRIMARY wymaga Androida 3.0 (poziom interfejsu API 11) lub nowszego, ustawienie wartości minSdkVersion aplikacji na 10 lub niższą powoduje ostrzeżenie Android Lint w Android Studio. Aby wyłączyć to ostrzeżenie, przed definicją FROM_COLUMNS dodaj adnotację @SuppressLint("InlinedApi").

Inicjowanie fragmentu

Zainicjuj interfejs Fragment. Dodaj pusty, publiczny konstruktor wymagany przez system Androida, i w metodzie wywołania onCreateView() rozszerz interfejs użytkownika obiektu Fragment. Na przykład:

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

Konfigurowanie obiektu CursorAdapter na potrzeby obiektu ListView

Skonfiguruj funkcję SimpleCursorAdapter, która wiąże wyniki funkcji wyszukaj ListView. Aby uzyskać obiekt ListView, który wyświetla kontakty, musisz wywołać funkcję Activity.findViewById(), korzystając z aktywności nadrzędnej obiektu Fragment. Gdy wywołujesz setAdapter(), używaj Context aktywności nadrzędnej. Na przykład:

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

Ustaw wybrany detektor kontaktów

Gdy wyświetlasz wyniki wyszukiwania, zwykle pozwalasz użytkownikom wybrać jednego kontaktu do dalszego przetworzenia. Na przykład po kliknięciu przez użytkownika kontaktu możesz wyświetlić adres kontaktu na mapie. Aby udostępnić tę funkcję, musisz najpierw zdefiniować bieżącą Fragment jako odbiornik kliknięć, określając, że klasa implementuje AdapterView.OnItemClickListener, jak pokazano w sekcji Zdefiniuj fragment, który wyświetla listę kontaktów.

Aby kontynuować konfigurowanie listenera, powiązać go z ListView, wywołując metodę setOnItemClickListener()onActivityCreated(). Na przykład:

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

Ponieważ określiłeś, że bieżąca wartość Fragment to OnItemClickListener dla ListView, musisz teraz zaimplementować wymaganą metodę onItemClick(), która obsługuje zdarzenie kliknięcia. Jest to opisane w następnej sekcji.

Definiowanie projekcji

Zdefiniuj stałą zawierającą kolumny, które mają być zwracane przez zapytanie. Każdy element w ListView wyświetla wyświetlaną nazwę kontaktu, który zawiera główną postać imienia i nazwiska osoby kontaktowej. W Androidzie 3.0 (poziom interfejsu API 11) i nowszych nazwa tej kolumny to Contacts.DISPLAY_NAME_PRIMARY; w wersjach starszych niż ta nazwa to Contacts.DISPLAY_NAME.

Kolumna Contacts._ID jest używana przez funkcję SimpleCursorAdapter. Contacts._ID i LOOKUP_KEY są używane razem do: utworzyć identyfikator URI treści dla kontaktu wybranego przez użytkownika.

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

        };

Zdefiniuj stałe indeksów kolumn Kursor

Aby uzyskać dane z pojedynczej kolumny w tabeli Cursor, musisz mieć indeksu kolumny w Cursor. Możesz zdefiniować stałe dla indeksów kolumn Cursor, ponieważ indeksy są takie same jak kolejność nazw kolumn w projekcji. Na przykład:

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;

Określ kryteria wyboru

Aby określić dane, które Cię interesują, utwórz kombinację wyrażeń tekstowych i zmiennych, które informują dostawcę o tym, w których kolumnach danych ma szukać i jakich wartości.

W wyrażeniu tekstowym zdefiniuj stałą, która będzie zawierać listę kolumn wyszukiwania. Chociaż to wyrażenie może też zawierać wartości, zaleca się, aby wartości były reprezentowane za pomocą zastępnika „?”. Podczas pobierania obiekt zastępczy jest zastępowany wartościami z . Używasz „?” pozwala wygenerować specyfikację wyszukiwania przez powiązanie a nie przez kompilację SQL. Takie rozwiązanie eliminuje ryzyko zainfekowania złośliwym kodem SQL wstrzyknięcie kodu. Na przykład:

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

Definiowanie metody onItemClick()

W poprzedniej sekcji ustawiasz detektor kliknięć elementu dla: ListView. Teraz zaimplementuj działanie dla detektora, definiując metodę AdapterView.OnItemClickListener.onItemClick():

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.
         */
    }

Inicjowanie ładowarki

Ponieważ do pobierania danych używasz CursorLoader, musisz zainicjować wątek w tle i inne zmienne, które kontrolują asynchroniczne pobieranie. Wykonaj inicjalizację w funkcji onCreate(), jak w tym przykładzie:

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

Wdróż onCreateLoader()

Zaimplementuj metodę onCreateLoader(), która jest wywoływana przez platformę ładowania zaraz po wywołaniu initLoader()

onCreateLoader() skonfiguruj wzór ciągu wyszukiwania. Aby zmienić ciąg znaków w wzór, wstaw znaki „%” (procenty) reprezentujące sekwencję co najmniej 1 znaku lub znaki „_” (podkreślenie) reprezentujące pojedynczy znak. Na przykład wzór „%Jefferson%” będzie pasować zarówno do „Thomas Jefferson”, jak i do „Jefferson Davis”.

Zwraca nowy obiekt CursorLoader z metody. Jeśli chodzi o treści, Identyfikator URI, użyj Contacts.CONTENT_URI. Ten identyfikator URI odnosi się do całej tabeli, jak pokazano w tym przykładzie:

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

Zaimplementuj metody onLoadFinished() i onLoaderReset().

Zaimplementuj tag onLoadFinished() . Platforma wczytywania wywołuje onLoadFinished() gdy dostawca kontaktów zwróci wyniki zapytania. W tej metodzie umieść wynik Cursor w metodzie SimpleCursorAdapter. Spowoduje to automatyczne zaktualizowanie pola ListView z wynikami wyszukiwania:

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

Metoda onLoaderReset() jest wywoływana, gdy platforma ładowania wykryje, że wynik Cursor zawiera nieaktualne dane. Usuń SimpleCursorAdapter odniesienie do istniejącego Cursor W przeciwnym razie framework loadera nie będzie mógł ponownie użyć obiektu Cursor, co spowoduje wyciek pamięci. Na przykład:

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

    }

Masz teraz najważniejsze elementy aplikacji, które pasują do wyszukiwanych słów w przypadku nazw i zwrotów kontaktów wynik w postaci ListView. Użytkownik może kliknąć nazwę kontaktu, aby go wybrać. Spowoduje to uruchomienie detektora, w którym możesz pracować z danymi kontaktu. Możesz na przykład pobrać dane kontaktu. Aby dowiedzieć się, jak to zrobić, przejdź do następnej lekcji, Pobieranie informacji o kontakcie.

Więcej informacji o interfejsach wyszukiwania znajdziesz w przewodniku po interfejsach API Tworzenie interfejsu wyszukiwania.

W pozostałych sekcjach tego wykładu omówiono inne sposoby znajdowania kontaktów Dostawca kontaktów.

Dopasowywanie kontaktu na podstawie określonego typu danych

Dzięki tej metodzie możesz określić typ danych, które chcesz dopasowywać. Pobieranie kontaktów według nazwy to konkretny przykład tego typu zapytania, ale możesz też stosować je do dowolnego typu danych szczegółowych powiązanych z kontaktem. Możesz na przykład pobrać kontakty, które mają określony kod pocztowy. W tym przypadku ciąg wyszukiwania musi pasować do danych zapisanych w wierszu kodu pocztowego.

Aby wdrożyć ten typ wyszukiwania, najpierw zastosuj ten kod, jak podano w poprzednich sekcjach:

  • Poproś o uprawnienia do odczytu dostawcy.
  • Definiowanie układów ListView i elementów.
  • Zdefiniuj fragment, który wyświetla listę kontaktów.
  • Zdefiniuj zmienne globalne.
  • Inicjalizacja fragmentu.
  • Skonfiguruj obiekt CursorAdapter na potrzeby widoku ListView.
  • Ustaw wybrany detektor kontaktów.
  • Określ stałe dla indeksów kolumn kursora.

    Chociaż pobierasz dane z innej tabeli, kolejność kolumn w projekcji jest taka sama, więc możesz używać tych samych indeksów dla kursora.

  • Zdefiniuj metodę onItemClick().
  • Zainicjuj program ładujący.
  • Implementacja onLoadFinished() i onLoaderReset().

W następnych krokach znajdziesz kod dodatkowy, który pozwoli Ci dopasować ciąg wyszukiwania do określonego typu danych szczegółowych i wyświetlić wyniki.

Wybierz typ danych i tabelę

Aby wyszukać określony typ danych szczegółowych, musisz znać niestandardową wartość typu MIME dla tego typu danych. Każdy typ danych ma niepowtarzalny typ MIME wartość zdefiniowana przez stałą CONTENT_ITEM_TYPE w podklasie Element ContactsContract.CommonDataKinds powiązany z typem danych. Podklasy mają nazwy wskazujące ich typ danych. Na przykład podklasa danych e-maila to ContactsContract.CommonDataKinds.Email, a niestandardowy typ MIME danych e-maila jest zdefiniowany przez stałą Email.CONTENT_ITEM_TYPE.

Do wyszukiwania użyj tabeli ContactsContract.Data. Wszystkie stałe potrzebne do projekcji, klauzuli wyboru i kolejności sortowania są zdefiniowane w tabeli lub odziedziczone przez nią.

Definiowanie prognozy

Aby zdefiniować projekcję, wybierz co najmniej 1 kolumnę zdefiniowaną w ContactsContract.Data lub klasy, z których dziedziczy. Dostawca kontaktów wykonuje domyślne złączenie tabeli ContactsContract.Data z innymi tabelami, zanim zwróci wiersze. Na przykład:

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

Definiowanie kryteriów wyszukiwania

Aby wyszukać ciąg w obrębie danych określonego typu, utwórz klauzulę wyboru z następujące:

  • Nazwa kolumny zawierającej wyszukiwany ciąg znaków. Ta nazwa różni się w zależności od typu danych, więc musisz znaleźć podklasę ContactsContract.CommonDataKinds, który odpowiada typowi danych a następnie wybierz nazwę kolumny z tej podklasy. Aby na przykład wyszukać adresy e-mail, użyj kolumny Email.ADDRESS.
  • sam ciąg znaków wyszukiwania, reprezentowany jako znak „?”; w klauzuli wyboru.
  • Nazwa kolumny zawierającej niestandardową wartość typu MIME. Ta nazwa jest zawsze Data.MIMETYPE.
  • Niestandardowa wartość typu MIME dla danego typu danych. Jak już wspomniano, jest to stała CONTENT_ITEM_TYPE w podklasie ContactsContract.CommonDataKinds. Na przykład wartość typu MIME dla danych e-maila to Email.CONTENT_ITEM_TYPE. Otocz wartość cudzysłowami, łącząc znak „'” (cudzysłów) na początku i na końcu stałej. W przeciwnym razie dostawca zinterpretuje ją jako nazwę zmiennej, a nie jako ciąg znaków. Dla tej wartości nie trzeba używać symbolu zastępczego, ponieważ używasz stałej zamiast wartości podanej przez użytkownika.

Na przykład:

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 + "'";

Następnie zdefiniuj zmienne zawierające argument wyboru:

Kotlin

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

Java

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

Wdróż onCreateLoader()

Po określeniu danych, których potrzebujesz, i sposobu ich znalezienia zdefiniuj zapytanie w implementacji usługi onCreateLoader(). Zwraca nową wartość CursorLoader z tej metody, używając jako argumentów projekcji, wyrażenia tekstowego w wyborze i tablicy wyboru. W przypadku identyfikatora URI treści użyj Data.CONTENT_URI Na przykład:

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

Te fragmenty kodu stanowią podstawę prostego wyszukiwania odwrotnego na podstawie określonego typu szczegółowych danych. Jest to najlepsza metoda, jeśli Twoja aplikacja koncentruje się na określonym typie danych, np. na e-mailach, i chcesz, aby użytkownicy mogli uzyskać nazwy powiązane z danymi.

Dopasowanie kontaktu według dowolnego typu danych

Pobieranie kontaktu na podstawie dowolnego typu danych zwraca kontakty, jeśli jakiekolwiek ich dane pasują do ciągu wyszukiwania, w tym imię i nazwisko, adres e-mail, adres pocztowy, numer telefonu itp. W ten sposób otrzymujesz szeroki zestaw wyników wyszukiwania. Jeśli na przykład ciąg wyszukiwania to „Nowak”, wyszukanie dowolnego typu danych zwróci kontakt „Jan Nowak”, a także kontakty z adresem „ul. Nowak”.

Aby wdrożyć ten typ wyszukiwania, najpierw zastosuj ten kod, jak podano w poprzednich sekcjach:

  • Poproś o uprawnienia do odczytu dostawcy.
  • Definiowanie układów ListView i elementów.
  • Zdefiniuj fragment, który wyświetla listę kontaktów.
  • Zdefiniuj zmienne globalne.
  • Inicjalizacja fragmentu.
  • Skonfiguruj obiekt CursorAdapter na potrzeby widoku ListView.
  • Ustaw odbiornik wybranego kontaktu.
  • Zdefiniuj prognozę.
  • Zdefiniuj stałe indeksów kolumn Kursor.

    W przypadku tego typu pobierania używasz tej samej tabeli, która została użyta w sekcji Dopasuj kontakt według nazwy i wymień wyniki. Użyj tych samych indeksów kolumn.

  • Zdefiniuj metodę onItemClick().
  • Zainicjuj program ładujący.
  • Implementacja onLoadFinished() i onLoaderReset().

W następnych krokach pokażemy Ci dodatkowy kod, którego potrzebujesz do dopasowania ciągu wyszukiwania do dowolnego typu danych i wyświetlenia wyników.

Usuwanie kryteriów wyboru

Nie definiuj stałych SELECTION ani zmiennej mSelectionArgs. Nie są one używane przy tego rodzaju pobierania.

Wdróż onCreateLoader()

Zaimplementuj metodę onCreateLoader(), zwracającą nowy obiekt CursorLoader. Nie trzeba konwertować łańcucha wyszukiwania na wzór, ponieważ dostawca kontaktów automatycznie. Jako podstawowy identyfikator URI użyj wartości Contacts.CONTENT_FILTER_URI, a dołączając do niego ciąg wyszukiwania, wywołaj funkcję Uri.withAppendedPath(). Użycie tego identyfikatora URI automatycznie uruchamia wyszukiwanie dowolnego typu danych, jak pokazano w tym przykładzie:

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

Te fragmenty kodu są podstawą aplikacji, która przeprowadza ogólne wyszukiwanie dostawcy kontaktów. Ta metoda jest przydatna w przypadku aplikacji, które chcą zaimplementować funkcję podobną do ekranu listy kontaktów w aplikacji Kontakty.