Este documento mostra como seguir o estilo do Google Glass e implementar as práticas recomendadas comuns de IU ao usar o GDK.
Tema do Google Glass
O Google Glass aplica um tema padrão ao Glassware para que permaneça consistente com o restante da interface do usuário. O tema tem as características abaixo:
- Usa a fonte Roboto
- Mostra atividades em tela cheia sem barra de status ou de ação
- Aplica o plano de fundo sólido e preto
Para aplicar o tema Glass, não declare um tema no manifesto do Android.
Se você tiver um estilo personalizado para partes do Glassware
e quiser o tema padrão do Glass para todo o restante,
herde o Theme.DeviceDefault
com o atributo parent
:
<resources>
<style name="CustomTheme" parent="@android:style/Theme.DeviceDefault">
<!-- Theme customization goes here. -->
</style>
</resources>
Consulte o guia do desenvolvedor Android sobre Estilos e temas para ver mais informações sobre como criar temas.
Cartões no estilo do vidro
A classe CardBuilder
cria cards bem formados considerando um conjunto de propriedades. Use os layouts
fornecidos pelo CardBuilder.Layout
sempre que possível para que o conteúdo pareça ser outro conteúdo do
Glass.
Para usar o app CardBuilder
:
- Crie uma instância de
CardBuilder
, fornecendo a ela o layout desejado deCardBuilder.Layout
. - Defina as propriedades do card, como o texto, a nota de rodapé e o carimbo de data/hora.
- Chame
CardBuilder.getView()
para converter o cartão em umView
do Android ouCardBuilder.getRemoteViews()
para converter em um objetoRemoteViews
. - Use a
View
nas suas atividades, layouts ou em umaCardScrollView
, ou use aRemoteViews
em umLiveCard
.
Recursos comuns da IU
Muitos dos layouts fornecidos por CardBuilder
oferecem suporte aos recursos comuns de
interface do usuário descritos abaixo. Consulte a documentação dos layouts individuais em
CardBuilder.Layout
para ver uma lista dos recursos compatíveis com cada tipo de card.
Ícone de atribuição
O ícone de atribuição é um ícone opcional de 36 × 36 pixels que aparece no canto inferior direito de um cartão e à direita do carimbo de data/hora. Para definir esse
ícone, chame
CardBuilder.setAttributionIcon()
para identificar o aplicativo, principalmente em cards ativos, para que o usuário possa ver rapidamente
e ver a fonte das informações nesse card.
Indicador de pilha
O indicador de pilha, controlado por
CardBuilder.showStackIndicator()
,
é uma dobra no canto superior direito de um card. Use-o como
um indicador visual de que seu cartão representa um pacote de outros cartões em que o
usuário pode tocar diretamente.
View view = new CardBuilder(context, CardBuilder.Layout.TEXT)
.setText("A stack indicator can be added to the corner of a card...")
.setAttributionIcon(R.drawable.ic_smile)
.showStackIndicator(true)
.getView();
Layouts
Os exemplos a seguir mostram os layouts disponíveis usando
CardBuilder
.
TEXT
e TEXT_FIXED
O layout CardBuilder.Layout.TEXT
mostra texto sem margens com um mosaico de imagens
opcional em segundo plano. O texto é redimensionado dinamicamente para se ajustar ao espaço disponível.
CardBuilder.Layout.TEXT_FIXED
é semelhante, mas corrige o texto para um tamanho menor.
View view1 = new CardBuilder(context, CardBuilder.Layout.TEXT)
.setText("This is the TEXT layout. The text size will adjust dynamically.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.getView();
View view2 = new CardBuilder(context, CardBuilder.Layout.TEXT)
.setText("You can also add images to the background of a TEXT card.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.image1)
.addImage(R.drawable.image2)
.addImage(R.drawable.image3)
.addImage(R.drawable.image4)
.addImage(R.drawable.image5)
.getView();
View view3 = new CardBuilder(context, CardBuilder.Layout.TEXT_FIXED)
.setText("This is the TEXT_FIXED layout. The text size is always the same.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.getView();
COLUMNS
e COLUMNS_FIXED
O layout
CardBuilder.Layout.COLUMNS
mostra um mosaico ou um ícone de imagem no
lado esquerdo do cartão e um texto no lado direito. O texto é dimensionado dinamicamente
para se ajustar ao espaço disponível. Para manter o tamanho do texto fixo, use
CardBuilder.Layout.COLUMNS_FIXED
.
View view1 = new CardBuilder(context, CardBuilder.Layout.COLUMNS)
.setText("This is the COLUMNS layout with dynamic text.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.image1)
.addImage(R.drawable.image2)
.addImage(R.drawable.image3)
.addImage(R.drawable.image4)
.addImage(R.drawable.image5)
.getView();
View view2 = new CardBuilder(context, CardBuilder.Layout.COLUMNS)
.setText("You can even put a centered icon on a COLUMNS card instead of a mosaic.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.setIcon(R.drawable.ic_wifi)
.getView();
View view3 = new CardBuilder(context, CardBuilder.Layout.COLUMNS_FIXED)
.setText("This is the COLUMNS_FIXED layout. The text size is always the same.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.image1)
.addImage(R.drawable.image2)
.addImage(R.drawable.image3)
.addImage(R.drawable.image4)
.addImage(R.drawable.image5)
.getView();
CAPTION
O layout
CardBuilder.Layout.CAPTION
tem um mosaico de imagens em segundo plano
e um breve texto de legenda alinhado na parte de baixo do card. Também é possível colocar um ícone
ao lado da legenda para representar, por exemplo, a identidade de uma pessoa
associada ao conteúdo do cartão.
View view1 = new CardBuilder(context, CardBuilder.Layout.CAPTION)
.setText("The caption layout.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.beach)
.setAttributionIcon(R.drawable.ic_smile)
.getView();
View view2 = new CardBuilder(context, CardBuilder.Layout.CAPTION)
.setText("The caption layout with an icon.")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.addImage(R.drawable.beach)
.setIcon(R.drawable.ic_avatar)
.setAttributionIcon(R.drawable.ic_smile)
.getView();
TITLE
O layout
CardBuilder.Layout.TITLE
tem um mosaico de imagens em segundo plano
com um título centralizado e ícone opcional na parte de baixo do card. Esse layout
geralmente é usado para representar contatos ou compartilhar destinos. A anotação e o carimbo de data/hora
não são compatíveis com esse layout.
View view = new CardBuilder(context, CardBuilder.Layout.TITLE)
.setText("TITLE Card")
.setIcon(R.drawable.ic_phone)
.addImage(R.drawable.beach)
.getView();
AUTHOR
Use o layout
CardBuilder.Layout.AUTHOR
para exibir uma mensagem ou conversa
com foco no autor. Ele é compatível com um mosaico de imagens em segundo plano,
um ícone usado como avatar do autor e um título e subtítulo em que é possível
listar informações de identificação.
View view = new CardBuilder(context, CardBuilder.Layout.AUTHOR)
.setText("The AUTHOR layout lets you display a message or conversation "
+ " with a focus on the author.")
.setIcon(R.drawable.ic_avatar)
.setHeading("Joe Lastname")
.setSubheading("Mountain View, California")
.setFootnote("This is the footnote")
.setTimestamp("just now")
.getView();
MENU
O layout
CardBuilder.Layout.MENU
é parecido com um menu padrão do Google Glass. Ele tem um
ícone e um título centralizados e uma nota de rodapé opcional. Use esse layout para
telas de confirmação (fazendo a transição de "Excluir" para "Excluído" depois que o usuário
selecionar um item de menu, por exemplo). Se você precisar de um menu real, use um
menu de opções padrão.
View view = new CardBuilder(context, CardBuilder.Layout.MENU)
.setText("MENU layout")
.setIcon(R.drawable.ic_phone)
.setFootnote("Optional menu description")
.getView();
EMBED_INSIDE
O layout
CardBuilder.Layout.EMBED_INSIDE
incorpora um XML de layout personalizado do seu próprio design ao modelo
de cartão padrão do Glass. Isso permite que você projete uma IU personalizada para o aplicativo, mas ainda
tenha o posicionamento correto da nota de rodapé, carimbo de data/hora, ícone de atribuição e
indicador da pilha de um cartão, se necessário.
Depois de chamar
CardBuilder.getView()
,
use
findViewById()
no resultado para acessar as visualizações no layout incorporado. Da mesma forma, se você
chamar
CardBuilder.getRemoteViews()
,
poderá manipular as visualizações do layout incorporado transmitindo os IDs diretamente
para os métodos setter
RemoteViews
.
View view = new CardBuilder(context, CardBuilder.Layout.EMBED_INSIDE)
.setEmbeddedLayout(R.layout.food_table)
.setFootnote("Foods you tracked")
.setTimestamp("today")
.getView();
TextView textView1 = (TextView) view.findViewById(R.id.text_view_1);
textView1.setText("Water");
// ...and so on
Para ver um exemplo mais detalhado, consulte o projeto ApiDemo do GitHub.
ALERT
O layout
CardBuilder.Layout.ALERT
contém um grande ícone centralizado com uma
mensagem principal e uma nota de rodapé. Use esse layout em uma
Dialog
para
mostrar uma mensagem informativa, um aviso ou um erro importante no Glassware.
O exemplo a seguir mostra uma implementação de AlertDialog
e dispensa o
cartão e abre as configurações de Wi-Fi quando o usuário toca no cartão:
- Crie uma classe que estenda o
Dialog
. - Crie o card usando o
CardBuilder
com o layoutCardBuilder.Layout.ALERT
e defina a visualização de conteúdo com esse card. (Opcional) Crie um
GestureDetector
para processar os gestos do usuário neste card.public class AlertDialog extends Dialog { private final DialogInterface.OnClickListener mOnClickListener; private final AudioManager mAudioManager; private final GestureDetector mGestureDetector; /** * Handles the tap gesture to call the dialog's * onClickListener if one is provided. */ private final GestureDetector.BaseListener mBaseListener = new GestureDetector.BaseListener() { @Override public boolean onGesture(Gesture gesture) { if (gesture == Gesture.TAP) { mAudioManager.playSoundEffect(Sounds.TAP); if (mOnClickListener != null) { // Since Glass dialogs do not have buttons, // the index passed to onClick is always 0. mOnClickListener.onClick(AlertDialog.this, 0); } return true; } return false; } }; public AlertDialog(Context context, int iconResId, int textResId, int footnoteResId, DialogInterface.OnClickListener onClickListener) { super(context); mOnClickListener = onClickListener; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mGestureDetector = new GestureDetector(context).setBaseListener(mBaseListener); setContentView(new CardBuilder(context, CardBuilder.Layout.ALERT) .setIcon(iconResId) .setText(textResId) .setFootnote(footnoteResId) .getView()); } /** Overridden to let the gesture detector handle a possible tap event. */ @Override public boolean onGenericMotionEvent(MotionEvent event) { return mGestureDetector.onMotionEvent(event) || super.onGenericMotionEvent(event); } }
(Opcional) Na sua atividade, implemente um
OnClickListener
para processar fluxos adicionais quando o usuário tocar. Para ver mais informações sobre como iniciar atividades de configurações, como Wi-Fi, consulte Configurações de inicialização.Chame o construtor
AlertDialog
para mostrar o card de alerta.public class MyActivity extends Activity { ... private final DialogInterface.OnClickListener mOnClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int button) { // Open WiFi Settings startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); } }; @Override protected void onCreate(Bundle bundle) { ... new AlertDialog(context, R.drawable.ic_cloud_sad_150, R.string.alert_text, R.string.alert_footnote_text, mOnClickListener).show(); ... } }
Layouts XML
Aqui estão dois layouts de cartão básicos que você pode usar se a classe CardBuilder não atender às suas necessidades.
Layout principal
Esse layout define o padding e o rodapé padrão de um card. Coloque suas próprias
visualizações na RelativeLayout
vazia.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<RelativeLayout
android:id="@+id/body_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/glass_card_body_height"
android:layout_marginLeft="@dimen/glass_card_margin"
android:layout_marginTop="@dimen/glass_card_margin"
android:layout_marginRight="@dimen/glass_card_margin"
tools:ignore="UselessLeaf"
>
<!-- Put your widgets inside this RelativeLayout. -->
</RelativeLayout>
<LinearLayout
android:id="@+id/footer_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left"
android:layout_marginLeft="@dimen/glass_card_margin"
android:layout_marginBottom="@dimen/glass_card_footer_margin"
android:layout_marginRight="@dimen/glass_card_margin"
android:orientation="horizontal"
>
<!-- The footer view will grow to fit as much content as possible while the
timestamp view keeps a fixed width. If the footer text is too long, it
will be ellipsized with a 40px margin between it and the timestamp. -->
<TextView
android:id="@+id/footer"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/glass_card_margin"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
</LinearLayout>
</FrameLayout>
Layout de coluna esquerda
Isso define uma coluna esquerda de 240 px e uma coluna direita de 400 px na forma de duas RelativeLayout
s
em que você pode colocar suas visualizações.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<RelativeLayout
android:id="@+id/left_column"
android:layout_width="@dimen/glass_card_left_column_width"
android:layout_height="match_parent"
>
<!-- Put widgets for the left column inside this RelativeLayout. -->
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="@dimen/glass_card_body_height"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/glass_card_two_column_margin"
android:layout_marginRight="@dimen/glass_card_margin"
android:layout_marginTop="@dimen/glass_card_margin"
android:layout_toRightOf="@+id/left_column"
tools:ignore="UselessLeaf"
>
<!-- Put widgets for the right column inside this RelativeLayout. -->
</RelativeLayout>
<LinearLayout
android:id="@+id/footer_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_gravity="bottom|left"
android:layout_marginBottom="@dimen/glass_card_footer_margin"
android:layout_marginLeft="@dimen/glass_card_two_column_margin"
android:layout_marginRight="@dimen/glass_card_margin"
android:layout_toRightOf="@+id/left_column"
android:orientation="horizontal"
>
<!--
The footer view will grow to fit as much content as possible while the
timestamp view keeps a fixed width. If the footer text is too long, it
will be ellipsized with a 40px margin between it and the timestamp.
-->
<TextView
android:id="@+id/footer"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/glass_card_margin"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
</LinearLayout>
</RelativeLayout>
Dimensões padrão
Use esse arquivo com os layouts anteriores ou seus próprios layouts para
aderir ao estilo Glass padrão. Crie esse arquivo como
res/values/dimens.xml
no projeto do Android.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- The recommended margin for the top, left, and right edges of a card. -->
<dimen name="glass_card_margin">40px</dimen>
<!-- The recommended margin between the bottom of the card and the footer. This is
an adjusted value so that the baseline of the text in the footer sits 40px
from the bottom of the card, matching the other margins. -->
<dimen name="glass_card_footer_margin">33px</dimen>
<!-- The recommended margin for the left column of the two-column card. -->
<dimen name="glass_card_two_column_margin">30px</dimen>
<!-- The maximum height of the body content inside a card. -->
<dimen name="glass_card_body_height">240px</dimen>
<!-- The width of the left column in the two-column layout. -->
<dimen name="glass_card_left_column_width">240px</dimen>
</resources>