Esta lição mostra como recuperar dados detalhados de um contato, por exemplo, endereços de e-mail, números de telefone etc. Esses são os detalhes que os usuários procuram quando recuperam um contato. Você pode fornecer a eles todos os detalhes de um contato ou apenas exibir detalhes de um tipo específico, como endereços de e-mail.
As etapas desta lição pressupõem que você já tem uma
linha ContactsContract.Contacts
para um contato escolhido pelo usuário.
A lição Como recuperar nomes de contatos mostra como
extrair uma lista de contatos.
Recuperar todos os detalhes de um contato
Para recuperar todos os detalhes de um contato, pesquise no
Tabela ContactsContract.Data
para todas as linhas que contêm a string
LOOKUP_KEY
. Essa coluna está disponível em
na tabela ContactsContract.Data
, porque os Contatos
O provedor faz uma mesclagem implícita entre ContactsContract.Contacts
tabela e a tabela ContactsContract.Data
. A coluna
LOOKUP_KEY
é descrita
em mais detalhes na lição Como recuperar nomes de contatos.
Observação: a recuperação de todos os detalhes de um contato reduz o desempenho de um
dispositivo, porque ele precisa recuperar todas as colunas do
ContactsContract.Data
. Considere o impacto no desempenho antes de
você usa essa técnica.
Solicitar permissões
Para ler do Provedor de contatos, seu app precisa ter
a permissão READ_CONTACTS
.
Para solicitar essa permissão, adicione o seguinte elemento filho de
<manifest>
ao arquivo de manifesto:
<uses-permission android:name="android.permission.READ_CONTACTS" />
Configurar uma projeção
Dependendo do tipo de dados que uma linha contém, ela só pode usar algumas colunas ou muitas delas. Além disso,
os dados estão em colunas diferentes, dependendo do tipo.
Para garantir que você tenha todas as colunas possíveis para todos os tipos de dados possíveis, é preciso adicionar todas as colunas
à sua projeção. Sempre recupere
Data._ID
se estiver vinculando o resultado
Cursor
a um ListView
. Caso contrário, a vinculação
não vai funcionar. Recuperar Data.MIMETYPE
também
para que você possa identificar o tipo de dados de cada linha recuperada. Exemplo:
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 };
Essa projeção recupera todas as colunas de uma linha na
tabela ContactsContract.Data
, usando os nomes de colunas definidos na
classe ContactsContract.Data
.
Opcionalmente, você também pode usar quaisquer outras constantes de coluna definidas em ou herdadas pelo
ContactsContract.Data
. No entanto, observe que as colunas
SYNC1
a SYNC4
precisam ser usadas pelos adaptadores
de sincronização. Portanto, os dados correspondentes não são úteis.
Definir os critérios de seleção
Defina uma constante para sua cláusula de seleção, uma matriz para conter os argumentos de seleção e uma
para manter o valor de seleção. Use a coluna Contacts.LOOKUP_KEY
para encontrar o contato. Exemplo:
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
O uso de "?" como um marcador na expressão de texto de seleção garante que a pesquisa resultante seja gerada por vinculação e não por compilação SQL. Essa abordagem elimina de injeção maliciosa de SQL.
Definir a ordem de classificação
Defina a ordem de classificação desejada no Cursor
resultante. Para
manter todas as linhas de um determinado tipo de dados juntas, ordenar por
Data.MIMETYPE
: Esse argumento de consulta
agrupa todas as linhas de e-mail, todas as linhas de telefone e assim por diante. Exemplo:
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;
Observação: alguns tipos de dados não usam um subtipo; portanto, não é possível classificá-los por subtipo.
Em vez disso, é necessário iterar o Cursor
retornado,
determinar o tipo de dados da linha atual e armazenar dados para as linhas que usam um subtipo. Quando
terminar de ler o cursor, você poderá classificar cada tipo de dados por subtipo e exibir os
resultados.
Inicializar o carregador
Sempre faça recuperações a partir do Provedor de contatos (e de todos os outros provedores de conteúdo) em uma
linha de execução em segundo plano. Use o framework do carregador definido pelo
a classe LoaderManager
e as
Interface LoaderManager.LoaderCallbacks
para usar em segundo plano
das recuperações.
Quando estiver tudo pronto para recuperar as linhas, inicialize o framework do carregador chamando
initLoader()
. Transmita um
identificador inteiro para o método. Esse identificador é transmitido para
métodos LoaderManager.LoaderCallbacks
. O identificador ajuda você
usar vários carregadores em um app, permitindo que você os diferencie.
O seguinte snippet mostra como inicializar o framework do carregador:
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);
Implementar onCreateLoader()
Implemente o método onCreateLoader()
, chamado pelo framework do carregador imediatamente após chamar
initLoader()
. Retorne um
CursorLoader
desse método. Como você está pesquisando
na tabela ContactsContract.Data
, use a constante
Data.CONTENT_URI
como o URI de conteúdo.
Exemplo:
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 ); }
Implementar onLoadFinished() e onLoaderReset()
Implemente o método
onLoadFinished()
. O framework do carregador chama
onLoadFinished()
quando o Provedor de contatos retorna os resultados da consulta. Exemplo:
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; ... } }
O método onLoaderReset()
é invocado quando o framework do carregador detecta que os dados que embasam o resultado
Cursor
foram modificados. Agora, remova todas as referências existentes
para o Cursor
ao defini-los como nulos. Caso contrário, o carregador
o framework não vai destruir o Cursor
antigo, e você vai receber uma memória
vazar. Exemplo:
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; }
Recuperar detalhes específicos de um contato
A recuperação de um tipo de dados específico para um contato, por exemplo, todos os e-mails, segue o mesmo padrão que o usado na recuperação de todos os detalhes. Essas são as únicas mudanças que você precisa fazer no código. listados em Recuperar todos os detalhes de um contato:
- Projeção
-
Modifique sua projeção para recuperar as colunas específicas ao
tipo de dados. Modifique também a projeção para usar as constantes de nome de coluna definidas na
subclasse
ContactsContract.CommonDataKinds
correspondente ao tipo de dados. - Seleção
-
Modifique o texto da seleção para pesquisar o
MIMETYPE
que é específico para seu tipo de dados. - Ordem de classificação
-
Como você está selecionando apenas um tipo de detalhe, não agrupe os dados retornados
Cursor
porData.MIMETYPE
.
Essas modificações são descritas nas seções a seguir.
Definir uma projeção
Defina as colunas que você quer recuperar usando as constantes de nome de coluna na subclasse.
de ContactsContract.CommonDataKinds
para o tipo de dados.
Se você planeja vincular o Cursor
a um ListView
,
não se esqueça de recuperar a coluna _ID
. Por exemplo, para recuperar dados de e-mail, defina o
seguinte projeção:
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 };
Observe que essa projeção usa os nomes de coluna definidos na classe
ContactsContract.CommonDataKinds.Email
, em vez dos nomes de coluna
definidos na classe ContactsContract.Data
. O uso dos nomes de colunas específicas de e-mail
torna o código mais legível.
Na projeção, você também pode usar qualquer uma das outras colunas definidas na
subclasse ContactsContract.CommonDataKinds
.
Definir critérios de seleção
Defina uma expressão de texto de pesquisa que recupere linhas para o LOOKUP_KEY
de um contato específico e o Data.MIMETYPE
dos detalhes que você quer. Coloque o valor MIMETYPE
entre
aspas simples concatenando um caractere "'
" (aspas simples) no início e no final
da constante. Caso contrário, o provedor interpretará a constante como um nome de variável, e não
como um valor de string. Não é preciso usar um marcador para esse valor, porque você está
usando uma constante em vez de um valor fornecido pelo usuário. Exemplo:
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 = { "" };
Definir uma ordem de classificação
Defina uma ordem de classificação para o Cursor
retornado. Como você está recuperando um
tipo de dados específico, omita a classificação em MIMETYPE
.
Em vez disso, se o tipo de dados detalhados que você estiver pesquisando incluir um subtipo, classifique-os por subtipo.
Por exemplo, para dados de e-mail que você pode classificar
Email.TYPE
:
Kotlin
private const val SORT_ORDER: String = "${Email.TYPE} ASC"
Java
private static final String SORT_ORDER = Email.TYPE + " ASC ";