En esta lección, se muestra cómo obtener una lista de contactos cuyos datos coinciden con toda la cadena de búsqueda o parte de ella, por medio de las siguientes técnicas:
- Hacer coincidir con los nombres de contactos
- Para obtener una lista de contactos, haz coincidir la cadena de búsqueda con todos los datos del nombre del contacto o parte de ellos. El Proveedor de contactos permite varias instancias del mismo nombre, por lo que esta técnica puede mostrar una lista de coincidencias.
- Hacer coincidir con un tipo específico de datos, como un número de teléfono
- Haz coincidir la cadena de búsqueda con un tipo de detalle específico para obtener una lista de contactos. datos, como una dirección de correo electrónico. Por ejemplo, esta técnica te permite enumerar todos los contactos cuya dirección de correo electrónico coincida con la cadena de búsqueda.
- Hacer coincidir con cualquier tipo de datos
- Hacer coincidir la cadena de búsqueda con cualquier tipo de datos detallados para obtener una lista de contactos incluyendo nombre, número de teléfono, dirección, dirección de correo electrónico, etc. Por ejemplo: esta técnica te permite aceptar cualquier tipo de datos en una cadena de búsqueda y, a continuación, enumerar contactos cuyos datos coinciden con la cadena.
Nota: En todos los ejemplos de esta lección, se usa un
CursorLoader
para recuperar datos de Contactos
Proveedor Un CursorLoader
ejecuta su consulta en un subproceso distinto del subproceso de IU. Esto garantiza que la consulta no ralentice la IU.
tiempos de respuesta y causar una mala experiencia del usuario. Para obtener más información, consulta el
clase de capacitación
Cómo cargar datos en segundo plano
Cómo solicitar permiso para leer datos del proveedor
Para realizar cualquier tipo de búsqueda en el Proveedor de contactos, tu app debe tener el permiso READ_CONTACTS
.
Para solicitarlo, agrega lo siguiente:
<uses-permission>
a tu archivo de manifiesto como un elemento secundario de
<manifest>
:
<uses-permission android:name="android.permission.READ_CONTACTS" />
Cómo hacer coincidir un contacto por nombre y enumerar los resultados
Esta técnica intenta hacer coincidir una cadena de búsqueda con el nombre de un contacto en la
Tabla de ContactsContract.Contacts
del proveedor de contactos. Por lo general, quieres mostrar los resultados en un elemento ListView
para permitir que el usuario elija una de las coincidencias.
Cómo definir diseños de elementos y ListView
Para mostrar los resultados de la búsqueda en un elemento ListView
, necesitas un archivo de diseño principal que defina la IU completa, incluido el ListView
, y un archivo de diseño de elementos que defina una línea de ListView
. Por ejemplo, podrías crear
el archivo de diseño principal res/layout/contacts_list_view.xml
con
el siguiente 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"/>
Este XML usa el widget ListView
integrado de Android: android:id/list
.
Define el archivo de diseño del elemento contacts_list_item.xml
con el siguiente 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"/>
Este XML usa el widget TextView
integrado de Android: android:text1
.
Nota: En esta lección, no se describe la IU para obtener una cadena de búsqueda del usuario, ya que es posible que quieras obtenerla indirectamente. Por ejemplo, puedes darle al usuario una opción para buscar contactos cuyo nombre coincida con una cadena de un mensaje de texto entrante.
Los dos archivos de diseño que escribiste definen una interfaz de usuario que muestra una
ListView
El siguiente paso es escribir código que use esta IU para mostrar un
lista de contactos.
Cómo definir un fragmento que muestre la lista de contactos
Para mostrar la lista de contactos, primero define un elemento Fragment
que se cargue por medio de una Activity
. El uso de un elemento Fragment
es una técnica más flexible, ya que puedes usar un elemento Fragment
para mostrar la lista y un segundo elemento Fragment
para mostrar los detalles de un contacto que el usuario elige de la lista. Con este enfoque, puedes combinar una de las técnicas que se presentan
esta lección con una de la clase
Cómo obtener detalles de un contacto
Para aprender a usar uno o más objetos Fragment
de un
un Activity
, lee la clase de capacitación
Cómo compilar una IU dinámica con fragmentos
Para ayudarte a escribir consultas con el Proveedor de contactos, el framework de Android ofrece una
de contratos llamada ContactsContract
, que define valores útiles
constantes y métodos para acceder al proveedor. Cuando usas esta clase, no tienes que
definen tus propias constantes para los URI de contenido, los nombres de las tablas o las columnas. Para usar esta clase, incluye la siguiente sentencia:
Kotlin
import android.provider.ContactsContract
Java
import android.provider.ContactsContract;
Dado que el código usa un CursorLoader
para recuperar datos
del proveedor, debes especificar que implementa la interfaz del cargador
LoaderManager.LoaderCallbacks
Además, para ayudar a detectar qué contacto selecciona el usuario de la lista de resultados de la búsqueda, implementa la interfaz de adaptador AdapterView.OnItemClickListener
. Por ejemplo:
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 {
Cómo definir variables globales
Define variables globales que se usan en otras partes del código:
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; ...
Nota: Debido a que Contacts.DISPLAY_NAME_PRIMARY
requiere Android 3.0 (API nivel 11) o versiones posteriores, configurar la minSdkVersion
de tu app en 10 o menos genera una advertencia de Android Lint en Android Studio. Para desactivar esta advertencia, agrega la anotación @SuppressLint("InlinedApi")
antes de la definición de FROM_COLUMNS
.
Cómo inicializar el fragmento
Inicializa el objeto Fragment
. Agrega el constructor público vacío
que requiere el sistema Android e inflar la Fragment
IU del método de devolución de llamada onCreateView()
Por ejemplo:
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); }
Cómo configurar el CursorAdapter para el elemento ListView
Configura el SimpleCursorAdapter
que vincula los resultados de
busca el ListView
. Para obtener el objeto ListView
, sigue estos pasos:
que muestra los contactos, deberás llamar a Activity.findViewById()
usando la actividad principal de
Fragment
Usa el Context
de las
actividad parental cuando llames a setAdapter()
.
Por ejemplo:
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); }
Cómo configurar el objeto de escucha del contacto seleccionado
Cuando muestras los resultados de una búsqueda, por lo general, quieres que el usuario pueda seleccionar un único contacto para continuar con el procesamiento. Por ejemplo, cuando el usuario hace clic en un contacto, puedes mostrar su dirección en un mapa. Para proporcionar esta función, primero definiste el
Fragment
como el objeto de escucha de clics especificando que la clase
implementa AdapterView.OnItemClickListener
, como se muestra en la sección
Define un fragmento que muestre la lista de contactos.
Para continuar configurando el objeto de escucha, vincúlalo a ListView
mediante una llamada al método setOnItemClickListener()
en onActivityCreated()
. Por ejemplo:
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); ... }
Dado que especificaste que el elemento Fragment
actual es OnItemClickListener
para el ListView
, ahora debes implementar su método obligatorio onItemClick()
, que controla el evento de clic. Este proceso se describe en la próxima sección.
Cómo definir una proyección
Define una constante que contenga las columnas que quieras mostrar como resultado de tu búsqueda. Cada elemento de ListView
muestra el nombre visible del contacto, que contiene la forma principal del nombre del contacto. En Android 3.0 (API nivel 11) y versiones posteriores, el nombre de esta columna es Contacts.DISPLAY_NAME_PRIMARY
; en versiones anteriores a esa, su nombre es Contacts.DISPLAY_NAME
.
El proceso de vinculación SimpleCursorAdapter
usa la columna Contacts._ID
.
Contacts._ID
y LOOKUP_KEY
se usan juntos para construir un URI de contenido para el contacto que selecciona el usuario.
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 };
Cómo definir constantes para los índices de las columnas del Cursor
Para obtener datos de una columna individual en un Cursor
, necesitas el índice de la columna dentro del Cursor
. Puedes definir constantes
para los índices de las columnas Cursor
, ya que los índices
igual que el orden de los nombres de columna en tu proyección. Por ejemplo:
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;
Cómo especificar los criterios de selección
Para especificar los datos que deseas, crea una combinación de variables y expresiones de texto que indiquen al proveedor en qué columnas de datos debe buscar y qué valores debe obtener.
Para la expresión de texto, define una constante que enumere las columnas de búsqueda. Aunque esta expresión también puede contener valores, la práctica preferida es representar los valores con un marcador de posición "?". Durante la recuperación, el marcador de posición se reemplaza por valores de un . Usa "?" como marcador de posición garantiza que la especificación de búsqueda se genere mediante la vinculación y no con una compilación de SQL. Esta práctica elimina la posibilidad de que el código y la inyección de software. Por ejemplo:
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 };
Cómo definir el método onItemClick()
En una sección anterior, estableciste un objeto de escucha para hacer clic en un elemento del elemento ListView
.
Ahora implementa la acción para el objeto de escucha definiendo el método
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. */ }
Cómo inicializar el cargador
Como usas un CursorLoader
para recuperar datos,
debes inicializar el subproceso en segundo plano y otras variables que controlan las
y la recuperación de datos. Realiza la inicialización en
onCreate()
como
como se muestra en el siguiente ejemplo:
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);
Cómo implementar onCreateLoader()
Implementa el método
onCreateLoader()
,
que el framework del cargador llama inmediatamente después de que llamas
initLoader()
En onCreateLoader()
,
configura el patrón de cadena de búsqueda. Para convertir una cadena en un patrón, inserta “%”
(porcentaje) de caracteres para representar una secuencia de cero o más caracteres, o "_" (guion bajo)
caracteres para representar un solo carácter, o ambos. Por ejemplo, el patrón "%Jefferson%" coincidiría con "Thomas Jefferson" y "Jefferson Davis".
Muestra un CursorLoader
nuevo desde el método. Para el URI de contenido, usa Contacts.CONTENT_URI
.
Este URI hace referencia a toda la tabla, como se muestra en el siguiente ejemplo:
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 ); }
Cómo implementar onLoadFinished() y onLoaderReset()
Implementa el método onLoadFinished()
. El framework del cargador llama
onLoadFinished()
Cuando el Proveedor de contactos devuelve los resultados de la consulta En este método, coloca el
resultado Cursor
SimpleCursorAdapter
Esto actualizará automáticamente
ListView
por los resultados de la búsqueda:
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); }
El método onLoaderReset()
se invoca cuando el framework del cargador detecta que el resultado Cursor
contiene datos inactivos. Borra la referencia SimpleCursorAdapter
al elemento Cursor
existente. Si no lo haces, el framework del cargador no
recicla el Cursor
, lo que provoca una fuga de memoria. Por ejemplo:
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); }
Ahora tienes las partes clave de una app que hace coincidir una cadena de búsqueda con nombres de contactos y devoluciones.
el resultado en una ListView
. El usuario puede hacer clic en un nombre de contacto para seleccionarlo.
De esta manera, se activa un objeto de escucha, en el que puedes realizar acciones adicionales con los datos del contacto, Por ejemplo:
puedes recuperar los detalles del contacto. Para aprender a hacerlo, continúa con la siguiente
Cómo obtener detalles de un contacto.
Para obtener más información sobre las interfaces de usuario de búsqueda, lee la guía de API Cómo crear una interfaz de búsqueda.
En las secciones restantes de esta lección, se muestran otras formas de buscar contactos en el Proveedor de contactos
Cómo hacer coincidir un contacto por un tipo específico de datos
Esta técnica te permite especificar el tipo de datos para el que quieres obtener coincidencias. La obtención por nombre es un ejemplo específico de este tipo de búsqueda, pero también puedes realizarla para cualquier tipo de datos detallados asociados con un contacto. Por ejemplo, puedes recuperar los contactos que tienen un código postal específico; en este caso, la cadena de búsqueda tiene que coincidir con los datos almacenados en un código postal fila.
Para implementar este tipo de recuperación, primero debes incluir el siguiente código, como se enumera en las secciones anteriores:
- Cómo solicitar permiso para leer datos del proveedor
- Cómo definir diseños de elementos y ListView
- Cómo definir un fragmento que muestre la lista de contactos
- Cómo definir variables globales
- Cómo inicializar el fragmento
- Cómo configurar el CursorAdapter para la ListView
- Cómo configurar el objeto de escucha del contacto seleccionado
-
Cómo definir constantes para los índices de columnas del Cursor
Si bien obtendrás datos de tablas diferentes, el orden de las columnas en la proyección será el mismo, por lo que podrás usar los mismos índices para el Cursor.
- Cómo definir el método onItemClick()
- Cómo inicializar el cargador
- Cómo implementar onLoadFinished() y onLoaderReset()
En los siguientes pasos, se muestra el código adicional que necesitas para hacer coincidir una cadena de búsqueda con un tipo específico de datos de detalles y mostrar los resultados.
Cómo seleccionar el tipo de datos y la tabla
Para buscar un tipo específico de datos detallados, debes conocer el valor del tipo de MIME personalizado que está asociado al tipo de datos. Cada tipo de datos tiene un tipo de MIME único
valor definido por una constante CONTENT_ITEM_TYPE
en la subclase de
ContactsContract.CommonDataKinds
asociado con el tipo de datos.
Las subclases tienen nombres que indican su tipo de datos; por ejemplo, la subclase para datos de correo electrónico es ContactsContract.CommonDataKinds.Email
y el tipo de MIME personalizado para datos de correo electrónico se define con la constante Email.CONTENT_ITEM_TYPE
.
Usa la tabla ContactsContract.Data
para tu búsqueda. Todas las constantes que necesitas para tu proyección, la cláusula de selección y el orden de clasificación se definen en esta tabla, o bien esta las hereda.
Cómo definir una proyección
Para definir una proyección, elige una o más de las columnas definidas en ContactsContract.Data
o las clases de las que se hereda. El
El proveedor de contactos realiza una unión implícita entre ContactsContract.Data
y otras tablas antes de que muestre filas. Por ejemplo:
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 };
Cómo definir criterios de búsqueda
Para buscar una cadena dentro de un tipo particular de datos, construye una cláusula de selección a partir de lo siguiente:
-
El nombre de la columna que contiene tu string de búsqueda: Este nombre varía según el tipo de datos, por lo que debes buscar la subclase de
ContactsContract.CommonDataKinds
que corresponda al tipo de datos y elegir el nombre de esa subclase. Por ejemplo, para buscar direcciones de correo electrónico, usa la columnaEmail.ADDRESS
. - La propia cadena de búsqueda, representada como el signo "?" en la cláusula de selección.
-
El nombre de la columna que contiene el valor de tipo MIME personalizado: Este nombre siempre es
Data.MIMETYPE
. -
El valor de tipo de MIME personalizado del tipo de datos: Como se describió anteriormente, esta es la constante
CONTENT_ITEM_TYPE
en la subclaseContactsContract.CommonDataKinds
. Por ejemplo, el MIME de tipo de datos de correo electrónico esEmail.CONTENT_ITEM_TYPE
Encierra el valor entre comillas simples concatenando una “'
” (comilla simple) al principio y al final de la constante; De lo contrario, el proveedor interpreta el valor como un nombre de variable en lugar de como un valor de cadena. Como usas una constante en lugar de un valor proporcionado por el usuario, no necesitas usar un marcador de posición para este valor.
Por ejemplo:
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 + "'";
A continuación, define variables para contener el argumento de selección:
Kotlin
private var searchString: String? = null private val selectionArgs: Array<String> = arrayOf("")
Java
String searchString; String[] selectionArgs = { "" };
Cómo implementar onCreateLoader()
Ahora que especificaste los datos que quieres y la forma de encontrarlos, define una búsqueda en tu implementación de onCreateLoader()
.
Devolver un CursorLoader
nuevo a partir de
usando tu proyección, la expresión de texto de selección y un array de selección como
argumentos. Para un URI de contenido, usa
Data.CONTENT_URI
Por ejemplo:
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 ); }
Estos fragmentos de código son la base de una búsqueda inversa simple que se basa en un tipo específico de datos detallados. Esta es la mejor técnica que puedes implementar si tu app se enfoca en un tipo específico de datos, como correos electrónicos, y quieres permitir que los usuarios obtengan los nombres asociados con una porción de datos.
Cómo hacer coincidir un contacto usando cualquier tipo de datos
La obtención de un contacto en función de cualquier tipo de datos muestra contactos cuando cualquiera de los datos coincide con una cadena de búsqueda, incluido el nombre, la dirección de correo electrónico, la dirección, el número de teléfono, entre otros. De esta manera, se obtiene un amplio conjunto de resultados de la búsqueda. Por ejemplo, si la cadena de búsqueda es "Doe" y buscas cualquier tipo de datos, se mostrará el contacto "John Doe", pero también los contactos que viven en la "calle Doe".
Para implementar este tipo de recuperación, primero implementa el siguiente código, tal como se indica en secciones anteriores:
- Cómo solicitar permiso para leer datos del proveedor
- Cómo definir diseños de elementos y ListView
- Cómo definir un fragmento que muestre la lista de contactos
- Cómo definir variables globales
- Cómo inicializar el fragmento
- Cómo configurar el CursorAdapter para la ListView
- Cómo configurar el objeto de escucha del contacto seleccionado
- Cómo definir una proyección
-
Cómo definir constantes para los índices de columnas del Cursor
Para este tipo de recuperación, usarás la misma tabla de la sección Cómo hacer coincidir un contacto por nombre y enumerar los resultados. Además, debes usar los mismos índices de columna.
- Cómo definir el método onItemClick()
- Cómo inicializar el cargador
- Cómo implementar onLoadFinished() y onLoaderReset()
En los siguientes pasos, se muestra el código adicional que necesitas para hacer coincidir una cadena de búsqueda con cualquier tipo de datos y mostrar los resultados.
Cómo quitar criterios de selección
No definas las constantes SELECTION
ni la variable mSelectionArgs
,
ya que no se usan en este tipo de obtención.
Cómo implementar onCreateLoader()
Implementa el método onCreateLoader()
y muestra un nuevo CursorLoader
.
No necesitas convertir la cadena de búsqueda en un patrón, ya que el Proveedor de contactos lo hace automáticamente. Usa
Contacts.CONTENT_FILTER_URI
como el URI base y anexar tu string de búsqueda mediante una llamada
Uri.withAppendedPath()
Usa este URI
activa automáticamente la búsqueda de cualquier tipo de datos, como se muestra en el siguiente ejemplo:
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 ); }
Estos fragmentos de código son la base de una app que realiza una búsqueda más amplia en el Proveedor de contactos. Esta técnica es útil para las aplicaciones que desean implementar una funcionalidad similar a la Pantalla de la lista de contactos de la app de Personas.