Pobieranie szczegółów kontaktu

Z tej lekcji dowiesz się, jak pobrać szczegółowe dane kontaktu, takie jak adresy e-mail, numery telefonów itd. To szczegółowe informacje, których szukają użytkownicy po odzyskiwaniu kontaktu. Możesz podać wszystkie szczegóły kontaktu lub tylko szczegóły określonego typu, np. adresy e-mail.

W krokach w tej lekcji zakładamy, że masz już wiersz ContactsContract.Contacts dotyczący kontaktu wybranego przez użytkownika. W wykładzie Pobieranie nazw kontaktów dowiesz się, jak pobrać listę kontaktów.

Pobieranie wszystkich szczegółów kontaktu

Aby pobrać wszystkie szczegóły kontaktu, wyszukaj w tabeli ContactsContract.Data wiersze zawierające jego LOOKUP_KEY. Ta kolumna jest dostępna w tabeli ContactsContract.Data, ponieważ dostawca kontaktów tworzy niejawne złączenie między tabelą ContactsContract.Contacts a tabelą ContactsContract.Data. Kolumna LOOKUP_KEY jest szczegółowo opisana w lekcji Pobieranie nazw kontaktów.

Uwaga: pobranie wszystkich informacji o kontakcie zmniejsza wydajność urządzenia, ponieważ musi ono pobrać wszystkie kolumny w tabeli ContactsContract.Data. Zanim skorzystasz z tej metody, weź pod uwagę wpływ na wydajność.

Poproś o uprawnienia

Aby odczytywać dane z dostawcy kontaktów, aplikacja musi mieć uprawnienie READ_CONTACTS. Aby poprosić o to uprawnienie, dodaj do pliku manifestu ten element podrzędny elementu <manifest>:

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

Skonfiguruj projekcję

W zależności od typu danych w wierszu może być widocznych tylko kilka kolumn lub wiele. Dodatkowo dane znajdują się w różnych kolumnach w zależności od ich typu. Aby mieć pewność, że otrzymasz wszystkie możliwe kolumny dla wszystkich możliwych typów danych, musisz dodać do prognozy wszystkie nazwy kolumn. Zawsze pobieraj Data._ID, jeśli wiążesz wynik Cursor z ListView. W przeciwnym razie powiązanie nie będzie działać. Pobierz również Data.MIMETYPE, aby móc identyfikować typ danych w każdym pobieranym wierszu. Na przykład:

Kotlin

private val PROJECTION: Array<out String> = arrayOf(
        ContactsContract.Data._ID,
        ContactsContract.Data.MIMETYPE,
        ContactsContract.Data.DATA1,
        ContactsContract.Data.DATA2,
        ContactsContract.Data.DATA3,
        ContactsContract.Data.DATA4,
        ContactsContract.Data.DATA5,
        ContactsContract.Data.DATA6,
        ContactsContract.Data.DATA7,
        ContactsContract.Data.DATA8,
        ContactsContract.Data.DATA9,
        ContactsContract.Data.DATA10,
        ContactsContract.Data.DATA11,
        ContactsContract.Data.DATA12,
        ContactsContract.Data.DATA13,
        ContactsContract.Data.DATA14,
        ContactsContract.Data.DATA15
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.Data._ID,
                ContactsContract.Data.MIMETYPE,
                ContactsContract.Data.DATA1,
                ContactsContract.Data.DATA2,
                ContactsContract.Data.DATA3,
                ContactsContract.Data.DATA4,
                ContactsContract.Data.DATA5,
                ContactsContract.Data.DATA6,
                ContactsContract.Data.DATA7,
                ContactsContract.Data.DATA8,
                ContactsContract.Data.DATA9,
                ContactsContract.Data.DATA10,
                ContactsContract.Data.DATA11,
                ContactsContract.Data.DATA12,
                ContactsContract.Data.DATA13,
                ContactsContract.Data.DATA14,
                ContactsContract.Data.DATA15
            };

To odwzorowanie pobiera wszystkie kolumny wiersza w tabeli ContactsContract.Data przy użyciu nazw kolumn zdefiniowanych w klasie ContactsContract.Data.

Opcjonalnie możesz też użyć dowolnych innych stałych kolumn zdefiniowanych w klasie ContactsContract.Data lub przez nią dziedziczonych. Zwróć jednak uwagę, że kolumny od SYNC1 do SYNC4 są przeznaczone dla adapterów synchronizacji, więc ich dane nie są przydatne.

Zdefiniuj kryteria wyboru

Zdefiniuj stałą klauzulę wyboru, tablicę do przechowywania argumentów wyboru i zmienną do przechowywania wartości wyboru. Znajdź kontakt w kolumnie Contacts.LOOKUP_KEY. Na przykład:

Kotlin

// Defines the selection clause
private const val SELECTION: String = "${ContactsContract.Data.LOOKUP_KEY} = ?"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")
/*
 * Defines a variable to contain the selection value. Once you
 * have the Cursor from the Contacts table, and you've selected
 * the desired row, move the row's LOOKUP_KEY value into this
 * variable.
 */
private var lookupKey: String? = null

Java

    // Defines the selection clause
    private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };
    /*
     * Defines a variable to contain the selection value. Once you
     * have the Cursor from the Contacts table, and you've selected
     * the desired row, move the row's LOOKUP_KEY value into this
     * variable.
     */
    private lateinit var lookupKey: String

Użycie symbolu zastępczego „?” w wyrażeniu tekstowym zaznaczenia gwarantuje, że wynik wyszukiwania będzie generowany przez powiązanie, a nie przez kompilację SQL. Takie podejście eliminuje możliwość wstrzyknięcia złośliwego kodu SQL.

Określanie kolejności sortowania

Określ kolejność sortowania w wyniku Cursor. Aby wszystkie wiersze określonego typu danych były przechowywane razem, sortuj według kolumny Data.MIMETYPE. Ten argument zapytania grupuje wszystkie wiersze e-maili razem, wszystkie wiersze z numerami telefonów itd. Na przykład:

Kotlin

/*
 * Defines a string that specifies a sort order of MIME type
 */
private const val SORT_ORDER = ContactsContract.Data.MIMETYPE

Java

    /*
     * Defines a string that specifies a sort order of MIME type
     */
    private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;

Uwaga: niektóre typy danych nie korzystają z podtypów, więc nie można według niego sortować danych. Zamiast tego musisz iterować zwrócone dane Cursor, określić typ danych bieżącego wiersza i przechowywać dane dla wierszy, które korzystają z podtypu. Po przeczytaniu kursora możesz posortować każdy typ danych według podtypu i wyświetlić wyniki.

Inicjowanie modułu wczytywania

Zawsze pobieraj dane od dostawcy kontaktów (i wszystkich innych dostawców treści) w wątku w tle. Do pobierania danych w tle używaj platformy Loader zdefiniowanej przez klasę LoaderManager i interfejsu LoaderManager.LoaderCallbacks.

Gdy wszystko będzie gotowe do pobrania wierszy, zainicjuj platformę ładowania, wywołując initLoader(). Przekaż do metody identyfikator liczby całkowitej. Jest on przekazywany do metod LoaderManager.LoaderCallbacks. Identyfikator pomaga używać wielu modułów wczytywania w aplikacji, umożliwiając ich rozróżnianie.

Ten fragment kodu pokazuje, jak zainicjować platformę ładowania:

Kotlin

// Defines a constant that identifies the loader
private const val DETAILS_QUERY_ID: Int = 0

class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Initializes the loader framework
        loaderManager.initLoader(DETAILS_QUERY_ID, null, this)

Java

public class DetailsFragment extends Fragment implements
        LoaderManager.LoaderCallbacks<Cursor> {
    ...
    // Defines a constant that identifies the loader
    static int DETAILS_QUERY_ID = 0;
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        // Initializes the loader framework
        getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);

Implementacja onCreateLoader()

Zaimplementuj metodę onCreateLoader(), która jest wywoływana przez platformę wczytywania natychmiast po wywołaniu initLoader(). Zwracaj CursorLoader z tej metody. Przeszukujesz tabelę ContactsContract.Data, więc jako identyfikatora URI treści użyj stałej Data.CONTENT_URI. Na przykład:

Kotlin

override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
    // Choose the proper action
    mLoader = when(loaderId) {
        DETAILS_QUERY_ID -> {
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey
            // Starts the query
            activity?.let {
                CursorLoader(
                        it,
                        ContactsContract.Data.CONTENT_URI,
                        PROJECTION,
                        SELECTION,
                        selectionArgs,
                        SORT_ORDER
                )
            }
        }
        ...
    }
    return mLoader
}

Java

@Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        // Choose the proper action
        switch (loaderId) {
            case DETAILS_QUERY_ID:
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey;
            // Starts the query
            CursorLoader mLoader =
                    new CursorLoader(
                            getActivity(),
                            ContactsContract.Data.CONTENT_URI,
                            PROJECTION,
                            SELECTION,
                            selectionArgs,
                            SORT_ORDER
                    );
    }

Implementacja onLoadFinished() i onLoaderReset()

Zaimplementuj metodę onLoadFinished(). Platforma ładowania wywołuje onLoadFinished(), gdy dostawca kontaktów zwraca wyniki zapytania. Na przykład:

Kotlin

    override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
        when(loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * Process the resulting Cursor here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                    /*
                     * Process the resulting Cursor here.
                     */
                }
                break;
            ...
        }
    }

Metoda onLoaderReset() jest wywoływana, gdy platforma ładowania wykryje, że dane będące podstawą wyniku Cursor uległy zmianie. Na tym etapie usuń wszelkie istniejące odwołania do funkcji Cursor, ustawiając je na wartość null. Jeśli tego nie zrobisz, platforma ładowania nie zniszczy starego Cursor i dojdzie do wycieku pamięci. Na przykład:

Kotlin

    override fun onLoaderReset(loader: Loader<Cursor>) {
        when (loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
                }
                break;
    }

Pobieranie szczegółowych informacji o kontakcie

Pobieranie określonego typu danych kontaktu, np. wszystkich e-maili, odbywa się tak samo jak pobieranie wszystkich szczegółów. To jedyne zmiany, jakie musisz wprowadzić w kodzie wymienionym w sekcji Pobieranie wszystkich szczegółów kontaktu:

Prognoza
Zmodyfikuj prognozę, aby pobierać kolumny specyficzne dla tego typu danych. Zmodyfikuj też prognozę tak, aby używać stałych nazw kolumn zdefiniowanych w podklasie ContactsContract.CommonDataKinds odpowiadającej typowi danych.
Zaznaczenie
Zmień tekst, aby wyszukać wartość MIMETYPE specyficzną dla Twojego typu danych.
Kolejność sortowania
Wybierasz tylko jeden typ szczegółu, więc nie grupuj zwróconych danych Cursor według Data.MIMETYPE.

Opis tych modyfikacji znajduje się w kolejnych sekcjach.

Zdefiniuj rzut

Zdefiniuj kolumny, które chcesz pobrać, używając stałych nazw kolumn z podklasy ContactsContract.CommonDataKinds jako typu danych. Jeśli zamierzasz powiązać obiekt Cursor z ListView, pamiętaj, aby pobrać kolumnę _ID. Aby na przykład pobrać dane poczty e-mail, określ to odwzorowanie:

Kotlin

private val PROJECTION: Array<String> = arrayOf(
        ContactsContract.CommonDataKinds.Email._ID,
        ContactsContract.CommonDataKinds.Email.ADDRESS,
        ContactsContract.CommonDataKinds.Email.TYPE,
        ContactsContract.CommonDataKinds.Email.LABEL
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.CommonDataKinds.Email._ID,
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.TYPE,
                ContactsContract.CommonDataKinds.Email.LABEL
            };

Zwróć uwagę, że to odwzorowanie używa nazw kolumn zdefiniowanych w klasie ContactsContract.CommonDataKinds.Email, a nie nazw kolumn zdefiniowanych w klasie ContactsContract.Data. Używanie nazw kolumn typowych dla adresów e-mail zwiększa czytelność kodu.

W prognozie możesz też użyć dowolnej z pozostałych kolumn zdefiniowanych w podklasie ContactsContract.CommonDataKinds.

Zdefiniuj kryteria wyboru

Zdefiniuj wyrażenie tekstowe, które będzie pobierać wiersze LOOKUP_KEY określonego kontaktu i Data.MIMETYPE wybranych szczegółów. Wartość MIMETYPE umieść w pojedynczych cudzysłowach, dołączając znak „'” (pojedynczy cudzysłów) na początku i na końcu stałej. W przeciwnym razie dostawca interpretuje stałą jako nazwę zmiennej, a nie 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

/*
 * Defines the selection clause. Search for a lookup key
 * and the Email MIME type
 */
private const val SELECTION =
        "${ContactsContract.Data.LOOKUP_KEY} = ? AND " +
        "${ContactsContract.Data.MIMETYPE} = '${Email.CONTENT_ITEM_TYPE}'"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")

Java

    /*
     * Defines the selection clause. Search for a lookup key
     * and the Email MIME type
     */
    private static final String SELECTION =
            Data.LOOKUP_KEY + " = ?" +
            " AND " +
            Data.MIMETYPE + " = " +
            "'" + Email.CONTENT_ITEM_TYPE + "'";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };

Definiowanie kolejności sortowania

Określ kolejność sortowania dla zwracanego elementu Cursor. Pobierasz dane o konkretnym typie, więc pomiń sortowanie według zbioru danych MIMETYPE. Jeśli typ danych szczegółowych, które szukasz, zawiera podtyp, przeprowadź sortowanie według niego. Na przykład w przypadku danych poczty e-mail możesz posortować je według kategorii Email.TYPE:

Kotlin

private const val SORT_ORDER: String = "${Email.TYPE} ASC"

Java

    private static final String SORT_ORDER = Email.TYPE + " ASC ";