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 wyszukiwanego ciągu, korzystając z tych metod:

Dopasuj nazwy kontaktów
Możesz pobrać listę kontaktów, dopasowując wyszukiwane hasło do całości lub części danych nazwy kontaktu. Dostawca kontaktów dopuszcza wiele instancji o tej samej nazwie, więc ta metoda może zwracać listę dopasowań.
pasują do określonego typu danych, np. numeru telefonu;
Możesz pobrać listę kontaktów, dopasowując wyszukiwany ciąg znaków do określonego typu danych szczegółowych, takich jak adres e-mail. Ta metoda umożliwia na przykład wyświetlenie listy wszystkich kontaktów, których adres e-mail pasuje do wyszukiwanego ciągu.
Dopasuj dowolny typ danych
Możesz pobrać listę kontaktów, dopasowując wyszukiwane hasło do dowolnego typu danych szczegółowych, takich jak imię i nazwisko, numer telefonu, adres pocztowy czy adres e-mail. Ta metoda umożliwia np. akceptowanie dowolnego typu danych dla wyszukiwanego ciągu i wyświetlanie listy kontaktów, w przypadku których dane pasują do ciągu.

Uwaga: we wszystkich przykładach w tej lekcji do pobierania danych z dostawcy kontaktów używana jest metoda CursorLoader. CursorLoader uruchamia swoje zapytanie w wątku niezależnym od wątku interfejsu. Dzięki temu zapytanie nie spowolni czasu odpowiedzi interfejsu użytkownika i nie pogorszy komfortu użytkowników. Więcej informacji znajdziesz w szkoleniu na temat wczytywania danych w tle na temat Androida.

Poproś o uprawnienia do odczytu dostawcy

Aby można było wyszukać dostawcę kontaktów, aplikacja musi mieć uprawnienie READ_CONTACTS. Aby o to poprosić, dodaj ten element <uses-permission> do pliku manifestu jako element podrzędny elementu <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 znaków do nazwy kontaktu lub kontaktów w tabeli ContactsContract.Contacts dostawcy kontaktów. Wyniki najlepiej wyświetlać w funkcji ListView, aby umożliwić użytkownikowi wybór spośród pasujących kontaktów.

Definiowanie układów elementów ListView i elementów

Aby wyświetlić wyniki wyszukiwania w elemencie ListView, potrzebujesz głównego pliku układu, który określa cały interfejs użytkownika, w tym ListView, oraz plik układu elementu, który określa 1 wiersz interfejsu 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 plik XML korzysta z wbudowanego widżetu ListView android:id/list na Androidzie.

Zdefiniuj plik układu elementu 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 korzysta z wbudowanego widżetu TextView android:text1 na Androidzie.

Uwaga: ta lekcja nie opisuje interfejsu do pobierania ciągu wyszukiwania od użytkownika, ponieważ może on być pobierany pośrednio. Możesz na przykład umożliwić użytkownikom wyszukiwanie kontaktów, których imię i nazwisko pasuje do ciągu znaków w wiadomości przychodzącej.

Dwa napisane przez Ciebie pliki układu definiują interfejs użytkownika ListView. Następnym krokiem jest napisanie kodu, który będzie używać tego interfejsu do wyświetlenia listy kontaktów.

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

Aby wyświetlić listę kontaktów, najpierw określ Fragment, który jest ładowany przez Activity. Interfejs Fragment jest bardziej elastyczny, ponieważ pozwala wyświetlić listę za pomocą 1 elementu Fragment, a drugi Fragment do wyświetlania szczegółów kontaktu wybranego z listy. Dzięki tej metodzie możesz połączyć jedną z technik omówionych w tej lekcji z techniką z lekcji Pobieranie szczegółów kontaktu.

Aby dowiedzieć się, jak użyć co najmniej 1 obiektu Fragment z klasy Activity, przeczytaj klasę treningową Tworzenie dynamicznego interfejsu za pomocą fragmentów.

Aby ułatwić zapisywanie zapytań do dostawcy kontaktów, platforma Androida udostępnia klasę umów o nazwie ContactsContract, która określa przydatne stałe i metody dostępu do dostawcy. Jeśli używasz tej klasy, nie musisz definiować własnych stałych dla identyfikatorów URI treści, nazw tabel ani kolumn. Aby użyć tej klasy, dołącz tę instrukcję:

Kotlin

import android.provider.ContactsContract

Java

import android.provider.ContactsContract;

Ponieważ kod pobiera dane od dostawcy za pomocą polecenia CursorLoader, musisz określić, że implementuje on interfejs wczytywania LoaderManager.LoaderCallbacks. Poza tym, aby łatwiej wykrywać, który kontakt wybiera użytkownik 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 {

Zdefiniuj zmienne globalne

Zdefiniuj zmienne globalne 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, więc ustawienie minSdkVersion aplikacji na 10 lub mniej spowoduje wygenerowanie ostrzeżenia Android Lint w Android Studio. Aby wyłączyć to ostrzeżenie, dodaj adnotację @SuppressLint("InlinedApi") przed definicją słowa FROM_COLUMNS.

Zainicjuj fragment

Zainicjuj Fragment. Dodaj pusty, publiczny konstruktor wymagany przez system Android i rozszerzaj 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 obiektu CursorAdapter na potrzeby elementu ListView

Skonfiguruj obiekt SimpleCursorAdapter, który wiąże wyniki wyszukiwania z elementem ListView. Aby pobrać obiekt ListView, który wyświetla kontakty, musisz wywołać Activity.findViewById(), korzystając z aktywności nadrzędnej Fragment. Aby wywołać setAdapter(), użyj Context aktywności rodzica. 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 odbiornik

Gdy wyświetlasz wyniki wyszukiwania, zwykle chcesz umożliwić użytkownikowi wybranie pojedynczego kontaktu do dalszego przetworzenia. Gdy na przykład użytkownik kliknie kontakt, możesz wyświetlić jego adres na mapie. Aby udostępnić tę funkcję, najpierw zdefiniowano bieżący Fragment jako detektor kliknięć, wskazując, że klasa implementuje AdapterView.OnItemClickListener, jak pokazano w sekcji Zdefiniuj fragment, który wyświetla listę kontaktów.

Aby kontynuować konfigurowanie detektora, powiąż go z ListView, wywołując metodę setOnItemClickListener() w 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ż wskazano, że bieżąca Fragment to OnItemClickListener dla zdarzenia ListView, musisz teraz zaimplementować jej wymaganą metodę onItemClick(), która obsługuje zdarzenie kliknięcia. Zostało to opisane w kolejnej sekcji.

Zdefiniuj rzut

Określ stałą zawierającą kolumny, które chcesz zwrócić w wyniku zapytania. Każda pozycja w ListView zawiera wyświetlaną nazwę kontaktu, która zawiera główną formę nazwy kontaktu. W Androidzie 3.0 (interfejs API w wersji 11) i nowszych ta kolumna nazywa się Contacts.DISPLAY_NAME_PRIMARY, a w poprzednich wersjach – Contacts.DISPLAY_NAME.

Kolumna Contacts._ID jest używana przez proces powiązania SimpleCursorAdapter. Parametry Contacts._ID i LOOKUP_KEY są używane razem do utworzenia identyfikatora 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 indeksy kolumny Kursor

Aby uzyskać dane z pojedynczej kolumny w elemencie Cursor, indeks kolumny musi znajdować się w obrębie typu Cursor. W kolumnach Cursor możesz zdefiniować stałe indeksy, ponieważ są one takie same jak nazwy kolumn w prognozie. 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ć potrzebne dane, utwórz kombinację wyrażeń tekstowych i zmiennych, które informują dostawcę o kolumnach danych do przeszukania i znalezionych wartościach.

W przypadku wyrażenia tekstowego zdefiniuj stałą zawierającą listę kolumn wyszukiwania. Chociaż to wyrażenie może również zawierać wartości, preferowaną metodą jest podawanie ich za pomocą symbolu zastępczego „?”. Podczas pobierania obiekt zastępczy jest zastępowany wartościami z tablicy. Użycie symbolu zastępczego „?” gwarantuje, że specyfikacja wyszukiwania jest generowana przez powiązanie, a nie przez kompilację SQL. Ta metoda eliminuje 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 ustawiłeś odbiornik kliknięć produktu dla: ListView. 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.
         */
    }

Zainicjuj narzędzie do ładowania

Do pobierania danych używasz interfejsu CursorLoader, dlatego musisz zainicjować wątek w tle i inne zmienne, które kontrolują asynchroniczne pobieranie. Wykonaj inicjalizację w zadaniu onCreate(), tak 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 onCreateLoader()

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

W onCreateLoader() skonfiguruj wzorzec ciągu wyszukiwania. Aby utworzyć ciąg znaków we wzorcu, wstaw znaki „%” (procent) reprezentujące sekwencję zerowej lub większej liczby znaków albo „_” (podkreślenia) do reprezentowania jednego znaku albo oba te znaki. Na przykład wzór „%Jefferson%” będzie pasować zarówno do „Thomas Jefferson”, jak i „Jefferson Davis”.

Zwraca nowy obiekt CursorLoader z tej metody. Jako identyfikator URI treści wpisz Contacts.CONTENT_URI. Ten 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(). Platforma ładowania wywołuje onLoadFinished(), gdy dostawca kontaktów zwraca wyniki zapytania. W tej metodzie wynik Cursor umieść w elemencie SimpleCursorAdapter. Spowoduje to automatyczne zaktualizowanie tabeli ListView o wynikach 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ń odwołanie do istniejącej instancji Cursor (SimpleCursorAdapter). Jeśli tego nie zrobisz, platforma wczytywania nie będzie ponownie wykorzystywać 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 ciągu wyszukiwania do nazw kontaktów i zwraca wynik w postaci ListView. Użytkownik może kliknąć nazwę kontaktu, aby go wybrać. Uruchomi się wtedy detektor, w którym możesz 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 szczegółów kontaktu.

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

W pozostałych sekcjach tej lekcji przedstawiliśmy inne sposoby znajdowania kontaktów u dostawcy kontaktów.

Dopasowanie kontaktu do określonego typu danych

Ta metoda umożliwia określenie typu danych, które mają być dopasowywane. Pobieranie według nazwy to konkretny przykład zapytania tego typu, ale możesz to też zrobić w przypadku dowolnego typu danych szczegółowych powiązanych z kontaktem. Możesz na przykład pobrać kontakty o określonym kodzie pocztowym. W tym przypadku wyszukiwany ciąg znaków musi odpowiadać danym przechowywanym w wierszu kodu pocztowego.

Aby wdrożyć ten typ pobierania, najpierw zaimplementuj ten kod zgodnie z informacjami w poprzednich sekcjach:

  • Poproś o uprawnienia do odczytu dostawcy.
  • Definiowanie układów listy i elementów.
  • Zdefiniuj fragment wyświetlający listę kontaktów.
  • Zdefiniuj zmienne globalne.
  • Zainicjuj fragment.
  • Skonfiguruj komponent CursorAdapter na potrzeby elementu ListView.
  • Ustaw wybrany odbiornik.
  • Zdefiniuj stałe indeksy kolumny Kursor.

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

  • Zdefiniuj metodę onItemClick().
  • Zainicjuj moduł ładowania.
  • Implementacja onLoadFinished() i onLoaderReset().

W podanych niżej krokach znajdziesz dodatkowy kod, który pozwoli Ci dopasować wyszukiwany ciąg znaków do określonego typu szczegółowych danych, a następnie wyświetlić wyniki.

Wybierz typ danych i tabelę

Aby wyszukać określony typ szczegółowych danych, musisz znać wartość niestandardowego 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 danym typem danych. Podklasy mają nazwy wskazujące ich typ danych, na przykład podklasa danych poczty e-mail to ContactsContract.CommonDataKinds.Email, a niestandardowy typ MIME danych poczty e-mail jest zdefiniowany przez stałą Email.CONTENT_ITEM_TYPE.

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

Zdefiniuj rzut

Aby zdefiniować rzut, wybierz co najmniej 1 kolumnę zdefiniowanej w zadaniu ContactsContract.Data lub klasy, z których dziedziczy ono działanie. Dostawca kontaktów wykonuje niejawne złączenie danych między tabelami ContactsContract.Data i 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 z tej opcji:

  • Nazwa kolumny zawierającej wyszukiwany ciąg znaków. Ta nazwa różni się w zależności od typu danych, musisz więc znaleźć podklasę ContactsContract.CommonDataKinds odpowiadającą mu, 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 jako znak „?” w klauzuli wyboru.
  • Nazwa kolumny zawierającej wartość niestandardowego typu MIME. Ta nazwa to zawsze Data.MIMETYPE.
  • Wartość niestandardowego 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 danych poczty e-mail to Email.CONTENT_ITEM_TYPE. Wartość umieść w pojedynczych cudzysłowach, łącząc znak „'” (pojedynczy cudzysłów) na początku i na końcu stałej. W przeciwnym razie dostawca interpretuje wartość jako nazwę zmiennej, a nie jako wartość ciągu znaków. W przypadku tej wartości nie musisz używać symbolu zastępczego, ponieważ zamiast wartości podanej przez użytkownika używasz stałej wartości.

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 = { "" };

Implementacja onCreateLoader()

Po wskazaniu odpowiednich danych i sposobie ich wyszukiwania zdefiniuj zapytanie w swojej implementacji onCreateLoader(). Zwracaj z tej metody nowy CursorLoader, używając jako argumentów prognozy, wyrażenia tekstowego wyboru i tablicy wyboru. W przypadku identyfikatora URI treści wpisz 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 są podstawą prostego wyszukiwania wstecznego na podstawie określonego typu danych szczegółowych. Jest to najlepsza metoda, jeśli Twoja aplikacja skupia się na określonym typie danych, np. e-mailach, i chcesz umożliwić użytkownikom powiązanie nazw z danymi.

Dopasowanie kontaktu do dowolnego typu danych

Pobieranie kontaktu na podstawie dowolnego typu danych zwraca kontakty, jeśli którekolwiek z ich danych pasują do wyszukiwanego ciągu, w tym imię i nazwisko, adres e-mail, adres pocztowy, numer telefonu itp. Powoduje to wyświetlenie obszernego zestawu wyników wyszukiwania. Jeśli np. wyszukiwany ciąg to „Kowalski”, wyszukiwanie dowolnego typu danych zwróci kontakt „Jan Kowalski”, a także kontakty z listy „Kowalski”.

Aby wdrożyć ten typ pobierania, najpierw zaimplementuj ten kod zgodnie z informacjami w poprzednich sekcjach:

  • Poproś o uprawnienia do odczytu dostawcy.
  • Definiowanie układów listy i elementów.
  • Zdefiniuj fragment wyświetlający listę kontaktów.
  • Zdefiniuj zmienne globalne.
  • Zainicjuj fragment.
  • Skonfiguruj komponent CursorAdapter na potrzeby elementu ListView.
  • Ustaw wybrany odbiornik.
  • Zdefiniuj rzut.
  • Zdefiniuj stałe indeksy kolumny Kursor.

    W tym rodzaju pobierania używasz tej samej tabeli co w sekcji Dopasuj kontakt według nazwy i podaj wyniki. Użyj tych samych indeksów kolumn.

  • Zdefiniuj metodę onItemClick().
  • Zainicjuj moduł ładowania.
  • Implementacja onLoadFinished() i onLoaderReset().

Poniżej znajdziesz dodatkowy kod, który pozwoli Ci dopasować wyszukiwany ciąg do dowolnego typu danych, a następnie wyświetlić wyniki.

Usuń kryteria wyboru

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

Implementacja onCreateLoader()

Zaimplementuj metodę onCreateLoader(), zwracając nową wartość CursorLoader. Nie musisz konwertować ciągu wyszukiwania na wzorzec, ponieważ dostawca kontaktów robi to automatycznie. Użyj Contacts.CONTENT_FILTER_URI jako podstawowego identyfikatora URI i dołącz do niego ciąg wyszukiwania, wywołując Uri.withAppendedPath(). Użycie tego identyfikatora URI automatycznie aktywuje wyszukiwanie dowolnego typu danych, jak 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 szerokie wyszukiwanie informacji o dostawcy kontaktów. Ta metoda jest przydatna w aplikacjach, które chcą wdrożyć funkcje podobne do ekranu z listą kontaktów aplikacji Osoby.