Pobieranie szczegółów kontaktu

W tej lekcji dowiesz się, jak pobierać szczegółowe dane kontaktowe, takie jak adresy e-mail czy numery telefonu. To są informacje, których użytkownicy szukają, gdy pobierają kontakt. Możesz podać wszystkie informacje o kontakcie lub tylko dane określonego typu, na przykład adresy e-mail.

W tej lekcji zakładamy, że masz już wiersz ContactsContract.Contacts z kontaktem wybranym przez użytkownika. W lekcji Pobieranie nazw kontaktów znajdziesz informacje o pobieraniu listy kontaktów.

Pobieranie wszystkich szczegółów kontaktu

Aby pobrać wszystkie szczegóły kontaktu, wyszukaj w tabeli ContactsContract.Data wiersze zawierające adres LOOKUP_KEY tego kontaktu. Ta kolumna jest dostępna w tabeli ContactsContract.Data, ponieważ dostawca danych Kontakty tworzy domyślne złączenie między tabelą ContactsContract.Contacts a tabelą ContactsContract.Data. Kolumna LOOKUP_KEY została szczegółowo omówiona w lekcji Pobieranie nazw kontaktów.

Uwaga: pobranie wszystkich szczegółów kontaktu zmniejsza wydajność urządzenia, ponieważ musi ono pobrać wszystkie kolumny w tabeli ContactsContract.Data. Zanim użyjesz tej metody, weź pod uwagę wpływ na wydajność.

Prośba o uprawnienia

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

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

Konfigurowanie prognozy

W zależności od typu danych wiersz może używać tylko kilku kolumn lub wielu. Poza tym dane są w różnych kolumnach w zależności od ich typu. Aby mieć pewność, że otrzymujesz wszystkie możliwe kolumny dla wszystkich możliwych typów danych, musisz dodać do projekcji wszystkie nazwy kolumn. Zawsze pobieraj dane z poziomu Data._ID, jeśli chcesz związać wynik Cursor z poziomu ListView. W przeciwnym razie powiązanie nie zadziała. Pobierz też Data.MIMETYPE, aby określić typ danych każdego pobieranego wiersza. 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 prognozowanie pobiera wszystkie kolumny w wierszu w tabeli ContactsContract.Data przy użyciu nazw kolumn zdefiniowanych w klasie ContactsContract.Data.

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

Definiowanie kryteriów wyboru

Zdefiniuj stałą dla klauzuli selekcji, tablicę na argumenty selekcji i zmienną na wartość selekcji. Aby znaleźć kontakt, użyj kolumny 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 znaku „?” jako zastępnika w wyrażeniu tekstowym selekcji zapewnia, że wynik wyszukiwania będzie generowany przez wiązanie, a nie kompilację SQL. Takie podejście eliminuje możliwość wstrzyknięcia złośliwego kodu SQL.

Definiowanie kolejności sortowania

Określ kolejność sortowania w wynikającym z tego wierszu Cursor. Aby zachować wszystkie wiersze danego typu danych razem, posortuj według Data.MIMETYPE. Ten argument zapytania grupuje wszystkie wiersze e-maili razem, wszystkie wiersze numerów 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 używają podtypu, więc nie można sortować według podtypu. Zamiast tego musisz przejść przez zwrócone Cursor, określić typ danych bieżącego wiersza i zapisać dane w przypadku wierszy, które używają podtypu. Po zakończeniu odczytywania kursora możesz posortować każdy typ danych według podtypu i wyświetlić wyniki.

Inicjowanie ładowarki

Zawsze pobieraj dane od dostawcy kontaktów (i od wszystkich innych dostawców treści) w wątku w tle. Do pobierania w tle możesz używać mechanizmu ładowania zdefiniowanego przez klasę LoaderManager i interfejs LoaderManager.LoaderCallbacks.

Gdy wszystko będzie gotowe do pobrania wierszy, zainicjuj framework ładowarki, wywołując funkcję initLoader(). Przekaż do metody identyfikator liczby całkowitej; ten identyfikator jest przekazywany do metod LoaderManager.LoaderCallbacks. Identyfikator ułatwia korzystanie z kilku ładowarek w aplikacji, ponieważ pozwala je od siebie odróżnić.

Ten fragment kodu pokazuje, jak zainicjować framework loadera:

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 metody onCreateLoader()

Zaimplementuj metodę onCreateLoader(), która jest wywoływana przez platformę ładowania zaraz po wywołaniu initLoader(). Zwracaj wartośćCursorLoader z tej metody. Ponieważ szukasz w tabeli ContactsContract.Data, użyj stałej Data.CONTENT_URI jako identyfikatora URI treści. 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
                    );
    }

Zaimplementuj metody onLoadFinished() i onLoaderReset().

Zaimplementuj metodę onLoadFinished(). Gdy dostawca kontaktów zwróci wyniki zapytania, framework loader wywołuje funkcję onLoadFinished(). 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 mechanizm ładowarki wykryje, że dane stanowiące podstawę wyniku Cursor uległy zmianie. W tym momencie usuń wszystkie istniejące odwołania do Cursor, ustawiając je na null. Jeśli tego nie zrobisz, framework ładowarki 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 o kontakcie, np. wszystkich e-maili, odbywa się w taki sam sposób jak pobieranie wszystkich szczegółów. Oto jedyne zmiany, które musisz wprowadzić w kodzie wymienionym w sekcji Pobieranie wszystkich szczegółów kontaktu:

Prognoza
Zmodyfikuj prognozę, aby pobrać kolumny właściwe dla danego typu danych. Zmodyfikuj też rzutowanie, aby używać stałych nazw kolumny zdefiniowanych w podklasie ContactsContract.CommonDataKinds odpowiadającej typowi danych.
Zaznaczenie
Zmodyfikuj tekst zaznaczenia, aby wyszukać wartość MIMETYPE, która jest odpowiednia dla Twojego typu danych.
Kolejność sortowania
Ponieważ wybierasz tylko jeden typ szczegółów, nie grupowaj zwracanych danych Cursor według Data.MIMETYPE.

Te zmiany są opisane w następnych sekcjach.

Definiowanie projekcji

Zdefiniuj kolumny, które chcesz pobrać, używając stałych nazw kolumn z podklasy ContactsContract.CommonDataKinds jako typu danych. Jeśli planujesz powiązać CursorListView, pamiętaj, aby pobrać kolumnę _ID. Aby na przykład pobrać dane e-maila, zdefiniuj tę projekcję:

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 w tym prognozie używane są nazwy kolumn zdefiniowane w klasie ContactsContract.CommonDataKinds.Email, a nie nazwy kolumn zdefiniowane w klasie ContactsContract.Data. Użycie nazw kolumn dotyczących adresu e-mail zwiększa czytelność kodu.

W projekcji możesz też używać dowolnych innych kolumn zdefiniowanych w podklasie ContactsContract.CommonDataKinds.

Definiowanie kryteriów wyboru

Zdefiniuj wyrażenie wyszukiwania tekstowego, które pobiera wiersze dotyczące konkretnego kontaktu LOOKUP_KEY i Data.MIMETYPE odpowiednich szczegółów. Otocz wartość MIMETYPE pojedynczymi cudzysłowami, łącząc znak „'” (pojedyncze cudzysłowy) na początku i na końcu stałej. W przeciwnym razie dostawca zinterpretuje stałą jako nazwę zmiennej, a nie jako ciąg znaków. Nie musisz używać symbolu zastępczego dla tej wartości, ponieważ używasz stałej wartości, a nie wartości podanej przez użytkownika. 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

Zdefiniuj kolejność sortowania zwróconych wartości Cursor. Pobierasz określony typ danych, więc pomiń sortowanie w polu MIMETYPE. Jeśli jednak typ danych, którego szukasz, zawiera podtyp, możesz posortować dane według niego. Na przykład w przypadku danych e-mail możesz sortować według tych kolumn:Email.TYPE:

Kotlin

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

Java

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