Esta página mostra como adicionar um QuickContactBadge
à IU e como vincular dados a ele. Um QuickContactBadge
é um widget que
aparece inicialmente como uma imagem em miniatura. Embora você possa usar qualquer Bitmap
para a imagem em miniatura, o usual é utilizar um Bitmap
decodificado da
imagem em miniatura da foto do contato.
A pequena imagem age como um controle. Quando os usuários tocam na imagem, o
QuickContactBadge
se expande em uma caixa de diálogo que contém o seguinte:
- Uma imagem grande
- A imagem grande associada ao contato ou, se nenhuma imagem estiver disponível, um gráfico de marcador.
- Ícones do app
- Um ícone de app para cada parte dos dados detalhados que podem ser processados por um app integrado. Por exemplo, se os detalhes do contato incluem um ou mais endereços de e-mail, um ícone de e-mail é exibido. Quando os usuários tocam no ícone, todos os endereços de e-mail do contato aparecem. Quando os usuários tocam em um dos endereços, o app de e-mail exibe uma tela para escrever uma mensagem para o endereço de e-mail selecionado.
A visualização QuickContactBadge
fornece acesso instantâneo aos detalhes de um contato
e uma maneira rápida de se comunicar com ele. Os usuários não precisam procurar
um contato, encontrar e copiar informações e colá-las no app apropriado. Em vez disso, eles
podem tocar no QuickContactBadge
, escolher o método de comunicação que
querem usar e enviar as informações desse método diretamente para o app apropriado.
Adicionar uma visualização de QuickContactBadge
Para adicionar um QuickContactBadge
, insira um elemento <QuickContactBadge>
no layout, conforme mostrado no exemplo a seguir:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> ... <QuickContactBadge android:id=@+id/quickbadge android:layout_height="wrap_content" android:layout_width="wrap_content" android:scaleType="centerCrop"/> ... </RelativeLayout>
Recuperar dados do provedor
Para mostrar um contato no QuickContactBadge
, é necessário um URI de conteúdo
para o contato e um Bitmap
para a imagem pequena. Você gera
o URI de conteúdo e o Bitmap
a partir de colunas recuperadas do
Provedor de contatos. Especifique essas colunas como parte da projeção que você usa para carregar dados no seu Cursor
.
Para o Android 3.0 (nível 11 da API) e versões mais recentes, inclua as seguintes colunas na sua projeção:
Para o Android 2.3.3 (API de nível 10) e versões anteriores, use as seguintes colunas:
Os exemplos nesta página pressupõem que foi carregada uma
Cursor
que contém essas colunas e outras
selecionadas. Para saber como recuperar colunas em um Cursor
, consulte
Recuperar uma lista de contatos.
Definir o URI do contato e a miniatura
Depois de ter as colunas necessárias, você pode vincular dados ao
QuickContactBadge
.
Definir o URI do contato
Para definir o URI de conteúdo do contato, chame getLookupUri(id,lookupKey)
para receber um CONTENT_LOOKUP_URI
e chame assignContactUri()
para definir o contato. Isso é mostrado neste exemplo:
Kotlin
// The Cursor that contains contact rows var cursor: Cursor? = null // The index of the _ID column in the Cursor var idColumn: Int = 0 // The index of the LOOKUP_KEY column in the Cursor var lookupKeyColumn: Int = 0 // A content URI for the desired contact var contactUri: Uri? = null // A handle to the QuickContactBadge view ... cursor?.let { cursor -> /* * Insert code here to move to the desired cursor row */ // Gets the _ID column index idColumn = cursor.getColumnIndex(ContactsContract.Contacts._ID) // Gets the LOOKUP_KEY index lookupKeyColumn = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY) // Gets a content URI for the contact contactUri = ContactsContract.Contacts.getLookupUri( cursor.getLong(idColumn), cursor.getString(lookupKeyColumn) ) binding.badge.assignContactUri(contactUri) }
Java
// The Cursor that contains contact rows Cursor cursor; // The index of the _ID column in the Cursor int idColumn; // The index of the LOOKUP_KEY column in the Cursor int lookupKeyColumn; // A content URI for the desired contact Uri contactUri; ... /* * Insert code here to move to the desired cursor row */ // Gets the _ID column index idColumn = cursor.getColumnIndex(ContactsContract.Contacts._ID); // Gets the LOOKUP_KEY index lookupKeyColumn = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY); // Gets a content URI for the contact contactUri = Contacts.getLookupUri( cursor.getLong(idColumn), cursor.getString(lookupKeyColumn) ); binding.badge.assignContactUri(contactUri);
Quando os usuários tocam no ícone QuickContactBadge
, os detalhes
do contato aparecem na caixa de diálogo.
Definir a miniatura da foto
A definição do URI do contato para o QuickContactBadge
não carrega automaticamente a foto em miniatura do contato. Para carregar a foto, informe um URI para a foto na linha Cursor
do contato, use-o para abrir o arquivo que contém a foto em miniatura compactada e leia o arquivo em um Bitmap
.
Observação:a coluna PHOTO_THUMBNAIL_URI
não está disponível nas versões da plataforma anteriores à 3.0. Para essas versões, você precisa recuperar o URI
da subtabela Contacts.Photo
.
Primeiro, configure variáveis para acessar Cursor
contendo as colunas Contacts._ID
e Contacts.LOOKUP_KEY
:
Kotlin
// The column in which to find the thumbnail ID var thumbnailColumn: Int = 0 /* * The thumbnail URI, expressed as a String. * Contacts Provider stores URIs as String values. */ var thumbnailUri: String? = null ... cursor?.let { cursor -> /* * Gets the photo thumbnail column index if * platform version >= Honeycomb */ thumbnailColumn = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI) // Otherwise, sets the thumbnail column to the _ID column } else { idColumn } /* * Assuming the current Cursor position is the contact you want, * gets the thumbnail ID */ thumbnailUri = cursor.getString(thumbnailColumn) }
Java
// The column in which to find the thumbnail ID int thumbnailColumn; /* * The thumbnail URI, expressed as a String. * Contacts Provider stores URIs as String values. */ String thumbnailUri; ... /* * Gets the photo thumbnail column index if * platform version >= Honeycomb */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { thumbnailColumn = cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI); // Otherwise, sets the thumbnail column to the _ID column } else { thumbnailColumn = idColumn; } /* * Assuming the current Cursor position is the contact you want, * gets the thumbnail ID */ thumbnailUri = cursor.getString(thumbnailColumn); ...
Defina um método que pegue os dados relacionados à foto do contato e as dimensões da
visualização de destino e retorne a miniatura de tamanho adequado em um
Bitmap
. Comece construindo um URI que aponte para a
miniatura:
Kotlin
/** * Load a contact photo thumbnail and return it as a Bitmap, * resizing the image to the provided image dimensions as needed. * @param photoData photo ID Prior to Honeycomb, the contact's _ID value. * For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI. * @return A thumbnail Bitmap, sized to the provided width and height. * Returns null if the thumbnail is not found. */ private fun loadContactPhotoThumbnail(photoData: String): Bitmap? { // Creates an asset file descriptor for the thumbnail file var afd: AssetFileDescriptor? = null // try-catch block for file not found try { // Creates a holder for the URI val thumbUri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // If Android 3.0 or later, // sets the URI from the incoming PHOTO_THUMBNAIL_URI Uri.parse(photoData) } else { // Prior to Android 3.0, constructs a photo Uri using _ID /* * Creates a contact URI from the Contacts content URI * incoming photoData (_ID) */ val contactUri: Uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, photoData) /* * Creates a photo URI by appending the content URI of * Contacts.Photo */ Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY) } /* * Retrieves an AssetFileDescriptor object for the thumbnail URI * using ContentResolver.openAssetFileDescriptor */ afd = activity?.contentResolver?.openAssetFileDescriptor(thumbUri, "r") /* * Gets a file descriptor from the asset file descriptor. * This object can be used across processes. */ return afd?.fileDescriptor?.let {fileDescriptor -> // Decodes the photo file and returns the result as a Bitmap // if the file descriptor is valid BitmapFactory.decodeFileDescriptor(fileDescriptor, null, null) } } catch (e: FileNotFoundException) { /* * Handle file not found errors */ null } finally { // In all cases, close the asset file descriptor try { afd?.close() } catch (e: IOException) { } } }
Java
/** * Load a contact photo thumbnail and return it as a Bitmap, * resizing the image to the provided image dimensions as needed. * @param photoData photo ID Prior to Honeycomb, the contact's _ID value. * For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI. * @return A thumbnail Bitmap, sized to the provided width and height. * Returns null if the thumbnail is not found. */ private Bitmap loadContactPhotoThumbnail(String photoData) { // Creates an asset file descriptor for the thumbnail file AssetFileDescriptor afd = null; // try-catch block for file not found try { // Creates a holder for the URI Uri thumbUri; // If Android 3.0 or later if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // Sets the URI from the incoming PHOTO_THUMBNAIL_URI thumbUri = Uri.parse(photoData); } else { // Prior to Android 3.0, constructs a photo Uri using _ID /* * Creates a contact URI from the Contacts content URI * incoming photoData (_ID) */ final Uri contactUri = Uri.withAppendedPath( ContactsContract.Contacts.CONTENT_URI, photoData); /* * Creates a photo URI by appending the content URI of * Contacts.Photo */ thumbUri = Uri.withAppendedPath( contactUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY); } /* * Retrieves an AssetFileDescriptor object for the thumbnail URI * using ContentResolver.openAssetFileDescriptor */ afd = getActivity().getContentResolver(). openAssetFileDescriptor(thumbUri, "r"); /* * Gets a file descriptor from the asset file descriptor. * This object can be used across processes. */ FileDescriptor fileDescriptor = afd.getFileDescriptor(); // Decodes the photo file and returns the result as a Bitmap // if the file descriptor is valid if (fileDescriptor != null) { // Decodes the bitmap return BitmapFactory.decodeFileDescriptor( fileDescriptor, null, null); } // If the file isn't found } catch (FileNotFoundException e) { /* * Handle file not found errors */ // In all cases, close the asset file descriptor } finally { if (afd != null) { try { afd.close(); } catch (IOException e) {} } } return null; }
Chame o método loadContactPhotoThumbnail()
no código para acessar a
miniatura Bitmap
e use o resultado para definir a miniatura da foto no
seu QuickContactBadge
:
Kotlin
... /* * Decodes the thumbnail file to a Bitmap */ mThumbnailUri?.also { thumbnailUri -> loadContactPhotoThumbnail(thumbnailUri).also { thumbnail -> /* * Sets the image in the QuickContactBadge. * QuickContactBadge inherits from ImageView. */ badge.setImageBitmap(thumbnail) } }
Java
... /* * Decodes the thumbnail file to a Bitmap */ Bitmap mThumbnail = loadContactPhotoThumbnail(thumbnailUri); /* * Sets the image in the QuickContactBadge. * QuickContactBadge inherits from ImageView. */ badge.setImageBitmap(mThumbnail);
Adicionar um QuickContactBadge a uma ListView
Um QuickContactBadge
é uma adição útil a um
ListView
que exibe uma lista de contatos. Use o
QuickContactBadge
para mostrar uma foto em miniatura para cada contato. Quando
os usuários tocam na miniatura, a caixa de diálogo QuickContactBadge
aparece.
Adicionar o elemento QuickContactBadge
Para começar, adicione um elemento de visualização QuickContactBadge
ao layout do item.
Por exemplo, se você quiser mostrar um QuickContactBadge
e um nome para
cada contato recuperado, coloque o seguinte XML em um arquivo de layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <QuickContactBadge android:id="@+id/quickcontact" android:layout_height="wrap_content" android:layout_width="wrap_content" android:scaleType="centerCrop"/> <TextView android:id="@+id/displayname" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/quickcontact" android:gravity="center_vertical" android:layout_alignParentRight="true" android:layout_alignParentTop="true"/> </RelativeLayout>
Nas seções a seguir, esse arquivo é chamado de contact_item_layout.xml
.
Configurar um CursorAdapter personalizado
Para vincular um CursorAdapter
a um ListView
que contenha um QuickContactBadge
, defina um adaptador personalizado que
estenda CursorAdapter
. Essa abordagem permite processar os
dados no Cursor
antes de vinculá-los ao
QuickContactBadge
. Essa abordagem também permite vincular várias
colunas Cursor
ao QuickContactBadge
. Nenhuma
dessas operações é possível em um CursorAdapter
normal.
A subclasse de CursorAdapter
definida por você precisa
modificar os seguintes métodos:
CursorAdapter.newView()
-
Infla um novo objeto
View
para manter o layout do item. Na substituição desse método, armazene alças nos objetosView
filhos do layout, incluindo oQuickContactBadge
filho. Ao adotar essa abordagem, você evita ter que colocar alças nos objetosView
filhos toda vez que inflar um novo layout.Você precisa modificar esse método para ter alças nos objetos
View
filhos individuais. Essa técnica permite controlar a vinculação emCursorAdapter.bindView()
. CursorAdapter.bindView()
-
Move os dados da linha
Cursor
atual para os objetosView
filhos do layout do item. Você precisa modificar esse método para vincular o URI e a miniatura do contato aoQuickContactBadge
. A implementação padrão permite apenas um mapeamento um para um entre uma coluna e umView
.
O snippet de código a seguir contém um exemplo de uma subclasse personalizada de
CursorAdapter
:
Definir o adaptador de lista personalizado
Defina a subclasse de
CursorAdapter
,
incluindo o construtor, e substitua
newView()
e
bindView()
:
Kotlin
/** * Defines a class that holds resource IDs of each item layout * row to prevent having to look them up each time data is * bound to a row */ private data class ViewHolder( internal var displayname: TextView? = null, internal var quickcontact: QuickContactBadge? = null ) /** * * */ private inner class ContactsAdapter( context: Context, val inflater: LayoutInflater = LayoutInflater.from(context) ) : CursorAdapter(context, null, 0) { ... override fun newView( context: Context, cursor: Cursor, viewGroup: ViewGroup ): View { /* Inflates the item layout. Stores view references * in a ViewHolder class to prevent having to look * them up each time bindView() is called. */ return ContactListLayoutBinding.inflate(inflater, viewGroup, false).also { binding -> view.tag = ViewHolder().apply { displayname = binding.displayname quickcontact = binding.quickcontact } }.root } ... override fun bindView(view: View?, context: Context?, cursor: Cursor?) { (view?.tag as? ViewHolder)?.also { holder -> cursor?.apply { ... // Sets the display name in the layout holder.displayname?.text = getString(displayNameIndex) ... /* * Generates a contact URI for the QuickContactBadge */ ContactsContract.Contacts.getLookupUri( getLong(idIndex), cursor.getString(lookupKeyIndex) ).also { contactUri -> holder.quickcontact?.assignContactUri(contactUri) } getString(photoDataIndex)?.also {photoData -> /* * Decodes the thumbnail file to a Bitmap. * The method loadContactPhotoThumbnail() is defined * in the section "Set the contact URI and thumbnail." */ loadContactPhotoThumbnail(photoData)?.also { thumbnailBitmap -> /* * Sets the image in the QuickContactBadge. * QuickContactBadge inherits from ImageView. */ holder.quickcontact?.setImageBitmap(thumbnailBitmap) } } } } } }
Java
private class ContactsAdapter extends CursorAdapter { private LayoutInflater inflater; ... public ContactsAdapter(Context context) { super(context, null, 0); /* * Gets an inflater that can instantiate * the ListView layout from the file */ inflater = LayoutInflater.from(context); ... } ... /** * Defines a class that holds resource IDs of each item layout * row to prevent having to look them up each time data is * bound to a row */ private class ViewHolder { TextView displayname; QuickContactBadge quickcontact; } ... @Override public View newView( Context context, Cursor cursor, ViewGroup viewGroup) { /* Inflates the item layout. Stores view references * in a ViewHolder class to prevent having to look * them up each time bindView() is called. */ final ContactListLayoutBinding binding = ContactListLayoutBinding.inflate(inflater, viewGroup, false); final ViewHolder holder = new ViewHolder(); holder.displayname = binding.displayName; holder.quickcontact = binding.quickContact; view.setTag(holder); return binding.root; } ... @Override public void bindView( View view, Context context, Cursor cursor) { final ViewHolder holder = (ViewHolder) view.getTag(); final String photoData = cursor.getString(photoDataIndex); final String displayName = cursor.getString(displayNameIndex); ... // Sets the display name in the layout holder.displayname = cursor.getString(displayNameIndex); ... /* * Generates a contact URI for the QuickContactBadge */ final Uri contactUri = Contacts.getLookupUri( cursor.getLong(idIndex), cursor.getString(lookupKeyIndex)); holder.quickcontact.assignContactUri(contactUri); String photoData = cursor.getString(photoDataIndex); /* * Decodes the thumbnail file to a Bitmap. * The method loadContactPhotoThumbnail() is defined * in the section "Set the contact URI and thumbnail." */ Bitmap thumbnailBitmap = loadContactPhotoThumbnail(photoData); /* * Sets the image in the QuickContactBadge. * QuickContactBadge inherits from ImageView. */ holder.quickcontact.setImageBitmap(thumbnailBitmap); }
Configurar variáveis
No código, configure variáveis, incluindo uma projeção de Cursor
que
inclua as colunas necessárias, conforme mostrado no exemplo a seguir.
Observação:os snippets de código a seguir usam o método
loadContactPhotoThumbnail()
, definido na seção
Definir o URI e a miniatura do contato.
Kotlin
/* * Defines a projection based on platform version. This ensures * that you retrieve the correct columns. */ 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 }, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { ContactsContract.Contacts.PHOTO_FILE_ID } else { /* * Although it's not necessary to include the * column twice, this keeps the number of * columns the same regardless of version */ ContactsContract.Contacts._ID } ) ... class ContactsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> { ... // Defines a ListView private val listView: ListView? = null // Defines a ContactsAdapter private val adapter: ContactsAdapter? = null ... // Defines a Cursor to contain the retrieved data private val cursor: Cursor? = null /* * As a shortcut, defines constants for the * column indexes in the Cursor. The index is * 0-based and always matches the column order * in the projection. */ // Column index of the _ID column private val idIndex = 0 // Column index of the LOOKUP_KEY column private val lookupKeyIndex = 1 // Column index of the display name column private val displayNameIndex = 3 /* * Column index of the photo data column. * It's PHOTO_THUMBNAIL_URI for Honeycomb and later, * and _ID for previous versions. */ private val photoDataIndex: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) 3 else 0 ...
Java
public class ContactsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { ... // Defines a ListView private ListView listView; // Defines a ContactsAdapter private ContactsAdapter adapter; ... // Defines a Cursor to contain the retrieved data private Cursor cursor; /* * Defines a projection based on platform version. This ensures * that you retrieve the correct columns. */ private static final String[] PROJECTION = { ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY, (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Contacts.DISPLAY_NAME_PRIMARY : ContactsContract.Contacts.DISPLAY_NAME (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Contacts.PHOTO_FILE_ID : /* * Although it's not necessary to include the * column twice, this keeps the number of * columns the same regardless of version */ ContactsContract.Contacts._ID }; /* * As a shortcut, defines constants for the * column indexes in the Cursor. The index is * 0-based and always matches the column order * in the projection. */ // Column index of the _ID column private int idIndex = 0; // Column index of the LOOKUP_KEY column private int lookupKeyIndex = 1; // Column index of the display name column private int displayNameIndex = 3; /* * Column index of the photo data column. * It's PHOTO_THUMBNAIL_URI for Honeycomb and later, * and _ID for previous versions. */ private int photoDataIndex = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? 3 : 0; ...
Configurar a ListView
Em Fragment.onCreate()
, instancie o adaptador de cursor personalizado e receba um identificador para o ListView
:
Kotlin
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return FragmentListViewBinding.inflate(...).let { binding -> ... /* * Gets a handle to the ListView in the file * contact_list_layout.xml */ listView = binding.contactList mAdapter?.also { listView?.adapter = it } ... }.root } ...
Java
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentListViewBinding binding = FragmentListViewBinding.inflate(...) ... /* * Gets a handle to the ListView in the file * contact_list_layout.xml */ if (binding.contactListView != null && adapter != null) { binding.contactListView.setAdapter(adapter); } ... } ...
Em onViewCreated()
, vincule o
ContactsAdapter
ao ListView
:
Kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) /* * Instantiates the subclass of * CursorAdapter */ mAdapter = activity?.let { ContactsAdapter(it).also { adapter -> // Sets up the adapter for the ListView listView?.adapter = adapter } } }
Java
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { ... /* * Instantiates the subclass of * CursorAdapter */ mAdapter = new ContactsAdapter(getActivity()); // Sets up the adapter for the ListView if (listView != null && mAdapter != null) { listView.setAdapter(mAdapter); } ... } ...
Quando você receber de volta um Cursor
contendo os dados de contatos, geralmente em onLoadFinished()
, chame swapCursor()
para mover os dados de Cursor
para ListView
. Isso exibe o QuickContactBadge
para cada entrada na lista de contatos.
Kotlin
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) { // When the loader has completed, swap the cursor into the adapter mAdapter?.swapCursor(cursor) }
Java
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { // When the loader has completed, swap the cursor into the adapter mAdapter.swapCursor(cursor); }
Ao vincular um Cursor
a um
ListView
com um CursorAdapter
(ou subclasse) e usar um CursorLoader
para carregar o
Cursor
, sempre limpe as referências ao Cursor
na implementação do
onLoaderReset()
.
Isso é mostrado neste exemplo:
Kotlin
override fun onLoaderReset(loader: Loader<Cursor>) { // Removes remaining reference to the previous Cursor adapter?.swapCursor(null) }
Java
@Override public void onLoaderReset(Loader<Cursor> loader) { // Removes remaining reference to the previous Cursor adapter.swapCursor(null); }