Pobieranie listy kontaktów

Z tego samouczka dowiesz się, jak pobrać listę kontaktów, których dane pasują do całego ciągu wyszukiwania lub jego części, korzystając z tych technik:

Dopasowywanie nazw kontaktów
Pobierz listę kontaktów, dopasowując ciąg wyszukiwania do wszystkich lub części danych nazwy kontaktu. 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 danych, np. adresu 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 imienia i nazwiska, numeru telefonu, adresu ulicy, adresu e-mail itp. Na przykład ta technika umożliwia akceptowanie dowolnego typu danych dla ciągu wyszukiwania, a następnie wyświetlanie listy kontaktów, których dane pasują do tego ciągu.

Uwaga: wszystkie przykłady w tym samouczku używają funkcji CursorLoader do pobierania danych z usługi Dostawca kontaktów. CursorLoader wykonuje zapytanie w wątku, który jest oddzielony od wątku interfejsu. Dzięki temu zapytanie nie wydłuży czasu odpowiedzi interfejsu użytkownika i nie pogorszy wrażenia użytkowników. Więcej informacji znajdziesz w sesji szkoleniowej na temat Androida Załadowanie danych w tle.

Prośba o dostęp do odczytu dostawcy

Aby można było wyszukiwać informacje o dostawcy kontaktów, aplikacja musi mieć uprawnienie READ_CONTACTS. Aby to zrobić, dodaj ten element <uses-permission> do pliku manifestu jako element podrzędny elementu <manifest>:

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

Dopasowywanie kontaktu według nazwy i wyświetlanie wyników

Ta metoda próbuje dopasować ciąg wyszukiwania do nazwy kontaktu lub kontaktów w tabeli ContactsContract.Contacts dostawcy kontaktów. Zazwyczaj warto wyświetlać wyniki w oknie ListView, aby umożliwić użytkownikowi wybranie spośród dopasowanych 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 z tym kodem 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

Zdefiniuj plik układu produktu 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 kod XML używa wbudowanego widżetu TextView na Androida. android:text1

Uwaga: ta lekcja nie opisuje interfejsu użytkownika do pobierania ciągu wyszukiwania od użytkownika, bo może się zdarzyć, że konieczne będzie uzyskanie tego ciągu znaków 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, który jest wczytywany przez Activity. Użycie Fragment jest bardziej elastycznym rozwiązaniem, ponieważ możesz użyć jednego Fragment do wyświetlenia listy, a drugiego Fragment do wyświetlenia szczegółów kontaktu, który użytkownik wybierze z listy. Dzięki temu możesz połączyć jedną z technik przedstawionych w tym samouczku z jedną z technik z samouczku 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 klasę umów o nazwie ContactsContract, która definiuje przydatne stałe i metody dostępu do dostawcy. Korzystając z tej klasy, nie musisz definiować własnych stałych dotyczących 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 używa funkcji CursorLoader do pobierania danych od dostawcy, musisz określić, że implementuje interfejs ładowarki:LoaderManager.LoaderCallbacks. Poza tym, aby ułatwić wykrywanie kontaktu wybranego przez użytkownika 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

Definiowanie zmiennych globalnych, 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: Contacts.DISPLAY_NAME_PRIMARY wymaga Androida w wersji 3.0 (interfejs API w wersji 11) lub nowszej, dlatego ustawienie minSdkVersion aplikacji na 10 lub mniej powoduje wygenerowanie ostrzeżenia Android Lint w Android Studio. Aby wyłączyć to ostrzeżenie, przed definicją FROM_COLUMNS dodaj adnotację @SuppressLint("InlinedApi").

Inicjowanie fragmentu

Inicjalizacja Fragment. Dodaj pusty, publiczny konstruktor wymagany przez system Android i zwiększ interfejs użytkownika obiektu Fragment w metodzie wywołania zwrotnego onCreateView(). 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 CursorAdaptera dla ListView

Skonfiguruj SimpleCursorAdapter, który łączy wyniki wyszukiwania z elementem ListView. Aby uzyskać obiekt ListView, który wyświetla kontakty, musisz wywołać Activity.findViewById(), używając 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 odbiornik wybranego kontaktu

Wyświetlając wyniki wyszukiwania, zwykle chcesz umożliwić użytkownikowi wybranie jednego kontaktu do dalszego przetwarzania. Gdy użytkownik kliknie kontakt, możesz wyświetlić jego adres na mapie. Aby udostępnić tę funkcję, musisz najpierw zdefiniować bieżący Fragment jako detektor kliknięć, określając, że klasa implementuje AdapterView.OnItemClickListener, jak pokazano w sekcji Definiowanie fragmentu, 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ś/aś, że bieżąca wartość Fragment jest wartością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 zawiera wyświetlaną nazwę kontaktu, która zawiera główną formę nazwy kontaktu. W Androidzie 3.0 (wersja interfejsu API 11) i nowszych ta kolumna ma nazwę Contacts.DISPLAY_NAME_PRIMARY, a w poprzednich wersjach nazwa tej kolumny to Contacts.DISPLAY_NAME.

Kolumna Contacts._ID jest używana w procesie powiązania SimpleCursorAdapter. Wartości Contacts._IDLOOKUP_KEY są używane razem do tworzenia identyfikatora URI treści dla wybranego przez użytkownika kontaktu.

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

        };

Definiowanie stałych wartości dla indeksów kolumny Cursor

Aby uzyskać dane z pojedynczej kolumny w tablicy Cursor, musisz podać indeks tej kolumny w tablicy 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 przypadku wyrażenia tekstowego zdefiniuj stałą, która 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 tablicy. Użycie znaku „?” jako zastępnika zapewnia, że specyfikacja wyszukiwania jest generowana przez powiązanie, a nie przez kompilację SQL. Dzięki temu eliminowana jest możliwość wstrzyknięcia złośliwego kodu SQL. 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 };

Zdefiniuj metodę onItemClick()

W poprzedniej sekcji ustawiliśmy dla elementu ListView listenera kliknięcia elementu. Teraz zaimplementuj działanie dla odbiornika, 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);

Implementacja metody onCreateLoader()

Zaimplementuj metodę onCreateLoader(), która jest wywoływana przez mechanizm ładowarki natychmiast po wywołaniu metody 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 wzorzec „%Jefferson%” będzie pasował do zarówno haseł „Jefferson”, jak i „Jan Kowalski”.

Zwraca nową wartość CursorLoader z metody. W przypadku identyfikatora URI treści użyj wartości Contacts.CONTENT_URI. Identyfikator URI odnosi się do całej tabeli, tak jak 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
        );
    }

Implementacja onLoadFinished() i onLoaderReset()

Zaimplementuj metodę onLoadFinished(). Gdy dostawca kontaktów zwróci wyniki zapytania, framework ładowarki wywoła funkcję onLoadFinished() W tej metodzie umieść wynik Cursor w elemencie 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 wczytywania wykryje, że wynik Cursor zawiera nieaktualne dane. Usuń odwołanie do istniejącego pliku SimpleCursorAdapter w pliku 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 już kluczowe elementy aplikacji, która dopasowuje ciąg wyszukiwania do nazw kontaktów i zwraca wynik w komponencie ListView. Użytkownik może kliknąć nazwę kontaktu, aby ją wybrać. Spowoduje to wywołanie listenera, w którym możesz dalej pracować z danymi kontaktu. Możesz na przykład pobrać szczegóły kontaktu. Aby dowiedzieć się, jak to zrobić, przejdź do następnej lekcji, Pobieranie informacji o kontakcie.

Więcej informacji o interfejsach użytkownika wyszukiwarki znajdziesz w przewodniku po interfejsach API Tworzenie interfejsu wyszukiwania.

Pozostałe sekcje tego samouczka pokazują inne sposoby znajdowania kontaktów w usługach dostawców 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 danych dostawcy.
  • Definiowanie widoku listy i układów elementów.
  • Zdefiniuj fragment, który wyświetla listę kontaktów.
  • Zdefiniuj zmienne globalne.
  • Zainicjuj fragment.
  • Skonfiguruj CursorAdapter dla ListView.
  • Ustaw odbiornik wybranego kontaktu.
  • Określ stałe dla indeksów kolumn kursora.

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

  • Zdefiniuj metodę onItemClick().
  • Inicjowanie ładowarki.
  • Zaimplementuj metody onLoadFinished() i onLoaderReset().

W następnych krokach znajdziesz kod dodatkowy, który pozwoli Ci dopasować ciąg wyszukiwania do konkretnego 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 unikalną wartość typu MIME zdefiniowaną przez stałą CONTENT_ITEM_TYPE w podklasie ContactsContract.CommonDataKinds powiązanej 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 wymagane w prognozie, klauzuli wyboru i kolejności sortowania są zdefiniowane w tej tabeli lub przez nią dziedziczone.

Definiowanie projekcji

Aby zdefiniować prognozę, wybierz co najmniej 1 kolumnę określoną w funkcji ContactsContract.Data lub klasy, z których ona 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
        };

Zdefiniuj kryteria wyszukiwania

Aby wyszukać ciąg znaków w danych określonego typu, utwórz klauzulę wyboru, korzystając z tych elementów:

  • Nazwa kolumny zawierającej ciąg wyszukiwania. Nazwa ta różni się w zależności od typu danych, dlatego musisz znaleźć podklasę ContactsContract.CommonDataKinds odpowiadającą typowi danych, a następnie wybrać nazwę kolumny z tej podklasy. Aby na przykład wyszukać adresy e-mail, użyj kolumny Email.ADDRESS.
  • Sam ciąg wyszukiwania, reprezentowany przez znak „?” w klauzuli wyboru.
  • Nazwa kolumny zawierającej niestandardową wartość typu MIME. Ta nazwa jest zawsze Data.MIMETYPE.
  • Niestandardowa wartość typu MIME dla typu danych. Jak już wspomnieliśmy, jest to stała CONTENT_ITEM_TYPE w podklasie ContactsContract.CommonDataKinds. Na przykład wartość typu MIME w przypadku danych poczty e-mail 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 musisz używać symbolu zastępczego, ponieważ używasz stałej, a nie 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, które będą zawierać argument selection:

Kotlin

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

Java

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

Implementacja metody 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 wartości 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 wstecznego na podstawie określonego typu danych szczegółowych. 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 efekcie uzyskuje się szeroki zbiór 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 danych dostawcy.
  • Definiowanie widoku listy i układów elementów.
  • Zdefiniuj fragment, który wyświetla listę kontaktów.
  • Zdefiniuj zmienne globalne.
  • Zainicjuj fragment.
  • Skonfiguruj CursorAdapter dla ListView.
  • Ustaw odbiornik wybranego kontaktu.
  • Zdefiniuj projekcję.
  • Określ stałe dla indeksów kolumn kursora.

    W przypadku tego typu pobierania używasz tej samej tabeli co w sekcji Dopasuj kontakt według imienia i nazwiska i wymień wyniki. Użyj tych samych indeksów kolumn.

  • Zdefiniuj metodę onItemClick().
  • Inicjowanie ładowarki.
  • Zaimplementuj metody onLoadFinished() i onLoaderReset().

W następnych krokach pokażemy Ci dodatkowy kod, który umożliwi Ci dopasowanie ciągu wyszukiwania do dowolnego typu danych i wyświetlenie wyników.

Usuwanie kryteriów wyboru

Nie definiuj stałych SELECTION ani zmiennej mSelectionArgs. W przypadku tego typu wyszukiwania nie są one używane.

Wdróż onCreateLoader()

Zaimplementuj metodę onCreateLoader(), która zwraca nowy obiekt CursorLoader. Nie musisz konwertować ciągu wyszukiwania na wzór, ponieważ dostawca kontaktów robi to automatycznie. Użyj Contacts.CONTENT_FILTER_URI jako podstawowego identyfikatora URI i dołącz do niego wyszukiwany ciąg, wywołując metodę 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 stanowią podstawę aplikacji, która przeprowadza ogólne wyszukiwanie w usługodawcy kontaktów. Ta metoda jest przydatna w przypadku aplikacji, które chcą zaimplementować funkcję podobną do ekranu listy kontaktów w aplikacji Kontakty.