Com o Google Glass, você pode criar interações avançadas com seus cartões, como rolagem e animações.
Rolar cards em atividades
A tela e o touchpad do Glass são ótimos para mostrar cartões deslizáveis,
como na linha do tempo do Glass. Se você estiver criando uma atividade, poderá criar
o mesmo tipo de efeito com o
widget
CardScrollView
.
- Implemente um
CardScrollAdapter
para fornecer cards aoCardScrollView
. Você pode criar uma hierarquia de visualização padrão ou usar a classeCardBuilder
. - Crie um
CardScrollView
que use oCardScrollAdapter
como o fornecedor dos cartões. - Defina a visualização do conteúdo da atividade como
CardScrollView
ou exiba aCardScrollView
em um layout.
Veja uma implementação simples que rola por três cards:
public class CardScrollActivity extends Activity {
private List<CardBuilder> mCards;
private CardScrollView mCardScrollView;
private ExampleCardScrollAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
createCards();
mCardScrollView = new CardScrollView(this);
mAdapter = new ExampleCardScrollAdapter();
mCardScrollView.setAdapter(mAdapter);
mCardScrollView.activate();
setContentView(mCardScrollView);
}
private void createCards() {
mCards = new ArrayList<CardBuilder>();
mCards.add(new CardBuilder(this, CardBuilder.Layout.TEXT)
.setText("This card has a footer.")
.setFootnote("I'm the footer!"));
mCards.add(new CardBuilder(this, CardBuilder.Layout.CAPTION)
.setText("This card has a puppy background image.")
.setFootnote("How can you resist?")
.addImage(R.drawable.puppy_bg));
mCards.add(new CardBuilder(this, CardBuilder.Layout.COLUMNS)
.setText("This card has a mosaic of puppies.")
.setFootnote("Aren't they precious?")
.addImage(R.drawable.puppy_small_1);
.addImage(R.drawable.puppy_small_2);
.addImage(R.drawable.puppy_small_3));
}
private class ExampleCardScrollAdapter extends CardScrollAdapter {
@Override
public int getPosition(Object item) {
return mCards.indexOf(item);
}
@Override
public int getCount() {
return mCards.size();
}
@Override
public Object getItem(int position) {
return mCards.get(position);
}
@Override
public int getViewTypeCount() {
return CardBuilder.getViewTypeCount();
}
@Override
public int getItemViewType(int position){
return mCards.get(position).getItemViewType();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return mCards.get(position).getView(convertView, parent);
}
}
}
Como interagir com cards de rolagem
Como
CardScrollView
estende AdapterView
,
é possível implementar os listeners padrão do Android.
- Chame o método
setOnItemClickListener()
herdado noCardScrollView
. - Implemente um
gerenciador
onItemClick()
para o evento de toque.
Esta é uma extensão do exemplo anterior que reproduz um som de toque quando você toca em um cartão:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
setupClickListener();
setContentView(mCardScrollView);
}
private void setupClickListener() {
mCardScrollView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
am.playSoundEffect(Sounds.TAP);
}
});
}
Como animar cards de rolagem
Há três animações disponíveis para cards de rolagem: navegação, inserção e exclusão.
- Implemente uma ação de inserção ou exclusão em um card em uma posição específica no conjunto de cards.
- Chame
animate()
e use um valor do tipo enumeradoCardScrollView.Animation
. Para exibir uma animação mais suave, remova todas as referências a
notifyDataSetChanged()
. O métodoanimate()
processa a atualização da visualização do conjunto de dados.private class ExampleCardScrollAdapter extends CardScrollAdapter { ... // Inserts a card into the adapter, without notifying. public void insertCardWithoutNotification(int position, CardBuilder card) { mCards.add(position, card); } } private void insertNewCard(int position, CardBuilder card) { // Insert new card in the adapter, but don't call // notifyDataSetChanged() yet. Instead, request proper animation // to inserted card from card scroller, which will notify the // adapter at the right time during the animation. mAdapter.insertCardWithoutNotification(position, card); mCardScrollView.animate(position, CardScrollView.Animation.INSERTION); }
Dicas de performance e implementação para cards de rolagem
Lembre-se das seguintes implicações de design e desempenho ao criar roladores de cards.
Ciclo de vida do cartão
Para melhorar o desempenho, um CardScrollView
carrega apenas um subconjunto dos cartões fornecidos por um
CardScrollAdapter
, que geralmente são visíveis para o usuário, entre outros.
Por isso, um cartão pode estar em qualquer um destes quatro estados gerais:
- Desanexado: a visualização de rolagem do card não precisa desse card no momento.
Você vai receber uma notificação do método
onDetachedToWindow()
se um cartão tiver sido anexado e removido. - Attached: a visualização de rolagem do card solicita o card do adaptador com
getView()
, porque está próximo de ser "ativado". Você recebe uma notificação do métodoonAttachedToWindow()
do cartão quando isso acontece. - Ativado: o cartão fica parcialmente visível para o usuário, mas a visualização de rolagem
do cartão não "selecionou" o cartão para exibição ao usuário. O
método 'isActivated()'
retorna
true
nesse caso. - Selecionado: o cartão está ocupando a tela
inteira do usuário. Chamar
getSelectedView()
retorna o cartão selecionado no momento. O métodoisSelected()
retorna verdadeiro nesse caso.
Se você estiver animando a visualização do seu cartão ou fazendo outras operações de alto custo,
inicie e interrompa as operações em
onAttachedToWindow()
e
onDetachedToWindow()
para economizar recursos.
Reciclagem de cartões
Quando um cartão deixa de ser anexado e é removido, o objeto de visualização associado a ele pode ser reciclado e usado por um cartão que está sendo anexado. Reciclar visualizações com informações atualizadas é muito mais eficiente do que criar novas visualizações.
Para aproveitar a reciclagem de cartões, implemente os métodos
getItemViewType()
,
getViewTypeCount()
e getView()
da classe
CardScrollAdapter
. Em seguida, use alguns dos métodos de conveniência na classe CardBuilder
para implementar a reciclagem no CardScrollAdapter
,
como no exemplo a seguir:
private List<CardBuilder> mCards;
...
/**
* Returns the number of view types for the CardBuilder class. The
* CardBuilder class has a convenience method that returns this value for
* you.
*/
@Override
public int getViewTypeCount() {
return CardBuilder.getViewTypeCount();
}
/**
* Returns the view type of this card, so the system can figure out
* if it can be recycled. The CardBuilder.getItemViewType() method
* returns it's own type.
*/
@Override
public int getItemViewType(int position){
return mCards.get(position).getItemViewType();
}
/**
* When requesting a card from the adapter, recycle the view if possible.
* The CardBuilder.getView() method automatically recycles the convertView
* it receives, if possible, or creates a new view if convertView is null or
* of the wrong type.
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return mCards.get(position).getView(convertView, parent);
}
Como implementar IDs de cartão estáveis
Quando um cartão é selecionado e está sendo exibido aos usuários, talvez você
não queira que as mudanças no adaptador subjacente afetem o cartão mostrado aos usuários
nesse momento. Por exemplo, se um usuário estiver visualizando um cartão selecionado e um cartão
for removido à esquerda desse cartão, o cartão
que o usuário está visualizando pode alternar para a esquerda, porque o
CardScrollAdapter
reatribui IDs ao conjunto de dados subjacente quando ocorrem mudanças, por padrão.
Se fizer sentido atribuir códigos exclusivos dos seus cartões, é possível manter
um ID consistente no conjunto de dados subjacente para evitar o problema mencionado acima.
Para fazer isso, substitua
hasStableIds()
e retorne true
. Isso especifica para o sistema que o
CardScrollAdapter
mantém os IDs estáveis nas mudanças do conjunto de dados. Além disso, implemente
getItemId()
para retornar o ID exclusivo adequado para os cartões no adaptador.
A implementação padrão retorna o índice de posição do cartão no adaptador,
que é instável por padrão.
CardScrollAdapter vazio
Quando você tem um conjunto de dados vazio para os adaptadores, a visualização padrão é mostrar
uma tela preta. Se você quiser exibir uma visualização diferente nesses casos,
não use setEmptyView()
.
Em vez disso, crie um único card no CardScrollAdapter
.
Resposta horizontal
Muitas imersões integradas no Google Glass fornecem feedback "deslocado" ao deslizar para frente e para trás não realizam uma ação. Por exemplo, você pode ver o feedback ao deslizar após tirar uma foto.
Se a imersão não usa gestos de deslizar horizontais para executar
funções específicas do app, ofereça esse efeito de embrulho encapsulando o
layout em uma
CardScrollView
que contenha um card.
Copie a seguinte classe auxiliar no seu projeto:
public class TuggableView extends CardScrollView { private final View mContentView; /** * Initializes a TuggableView that uses the specified layout * resource for its user interface. */ public TuggableView(Context context, int layoutResId) { this(context, LayoutInflater.from(context) .inflate(layoutResId, null)); } /** * Initializes a TuggableView that uses the specified view * for its user interface. */ public TuggableView(Context context, View view) { super(context); mContentView = view; setAdapter(new SingleCardAdapter()); activate(); } /** * Overridden to return false so that all motion events still * bubble up to the activity's onGenericMotionEvent() method after * they are handled by the card scroller. This allows the activity * to handle TAP gestures using a GestureDetector instead of the * card scroller's OnItemClickedListener. */ @Override protected boolean dispatchGenericFocusedEvent(MotionEvent event) { super.dispatchGenericFocusedEvent(event); return false; } /** Holds the single "card" inside the card scroll view. */ private class SingleCardAdapter extends CardScrollAdapter { @Override public int getPosition(Object item) { return 0; } @Override public int getCount() { return 1; } @Override public Object getItem(int position) { return mContentView; } @Override public View getView(int position, View recycleView, ViewGroup parent) { return mContentView; } } }
Modifique o método
onCreate
na sua atividade para exibir oCardScrollView
que contém o layout.@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // was: setContentView(R.layout.main_activity); setContentView(new TuggableView(this, R.layout.main_activity)); }