Mit Glass können Sie umfassende Interaktionen mit Ihren Karten erstellen, z. B. Scrollen und Animationen.
Scrollen von Karten in Aktivitäten
Das Glass-Display und -Touchpad eignen sich hervorragend für die Anzeige von Karten, die schwenkbar sind, etwa auf der Glass-Zeitachse. Wenn Sie eine Aktivität erstellen, können Sie die gleiche Art von Effekt mit dem CardScrollView
-Widget erzeugen.
- Implementiere eine
CardScrollAdapter
, um Karten für denCardScrollView
bereitzustellen. Sie können selbst eine Standardansichtshierarchie erstellen oder die KlasseCardBuilder
verwenden. - Erstelle eine
CardScrollView
, für dieCardScrollAdapter
als Anbieter für Karten verwendet wird. - Lege die Inhaltsansicht deiner Aktivität als
CardScrollView
fest oder lasseCardScrollView
in einem Layout anzeigen.
Hier sehen Sie eine einfache Implementierung, bei der durch drei Karten gescrollt wird:
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);
}
}
}
Mit scrollbaren Karten interagieren
Da durch CardScrollView
AdapterView
erweitert wird, kannst du die standardmäßigen Android-Listener implementieren.
- Rufen Sie das übernommene
setOnItemClickListener()
in IhrerCardScrollView
auf. - Implementiere einen
onItemClick()
-Handler für das Tippereignis.
Hier ist eine Erweiterung des vorherigen Beispiels, die beim Tippen auf eine Karte einen Tippton wiedergibt:
@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);
}
});
}
Scrollende Karten animieren
Für das Scrollen von Karten stehen drei Animationen zur Verfügung: „Navigation“, „Insert“ (Einfügen) und „Deletion“ (Löschen).
- Implementieren Sie eine Einfüge- oder Löschaktion für eine Karte an einer bestimmten Position im Kartensatz.
- Rufen Sie
animate()
auf und verwenden Sie einen Wert aus dem EnumCardScrollView.Animation
. Entfernen Sie alle Verweise auf
notifyDataSetChanged()
, um die Animation flüssiger darzustellen. Die Methodeanimate()
übernimmt die Aktualisierung der Datensatzansicht.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); }
Leistungs- und Implementierungstipps für scrollbare Karten
Beachten Sie beim Erstellen von Karten-Scrollern die folgenden Auswirkungen auf Design und Leistung.
Lebenszyklus der Karte
Zur Steigerung der Leistung lädt ein CardScrollView
nur einen Teil der Karten, die von CardScrollAdapter
bereitgestellt werden (in der Regel die Karten, die für den Nutzer sichtbar sind, und einige weitere).
Aus diesem Grund kann eine Karte einen der folgenden vier allgemeinen Status haben:
- Detached (Losgelöst): Für die Scroll-Ansicht der Karte wird diese Karte derzeit nicht benötigt.
Sie werden über die Methode
onDetachedToWindow()
der Karte benachrichtigt, wenn zuvor eine Karte angehängt und dann getrennt wurde. - Angehängt: In der Scrollansicht der Karte wird die Karte vom Adapter mit
getView()
angefordert, da die Karte in Kürze „aktiviert“ ist. In diesem Fall werden Sie mit der MethodeonAttachedToWindow()
der Karte benachrichtigt. - Aktiviert: Die Karte ist für den Nutzer teilweise sichtbar, aber in der Scrollansicht der Karte wurde die Karte nicht „ausgewählt“, die dem Nutzer angezeigt werden soll. Die Methode 'isActivated()' gibt in diesem Fall
true
zurück. - Ausgewählt: Die Karte belegt den gesamten Bildschirm des Nutzers. Wenn Sie
getSelectedView()
aufrufen, wird die aktuell ausgewählte Karte zurückgegeben. Die MethodeisSelected()
gibt in diesem Fall „true“ zurück.
Wenn Sie die Ansicht Ihrer Karte animieren oder andere kostspielige Vorgänge ausführen, starten und beenden Sie die Vorgänge in onAttachedToWindow()
und onDetachedToWindow()
, um Ressourcen zu sparen.
Kartenrecycling
Wenn eine Karte nicht mehr angehängt wird, kann das mit der Karte verknüpfte Ansichtsobjekt recycelt und von einer gerade verbundenen Karte verwendet werden. Das Recycling von Ansichten mit aktualisierten Informationen ist viel effizienter als das Erstellen neuer Ansichten.
Implementieren Sie die Methoden getItemViewType()
, getViewTypeCount()
und getView()
der Klasse CardScrollAdapter
, um die Vorteile des Kartenrecycling zu nutzen. Anschließend verwenden Sie einige der praktischen Methoden aus der Klasse CardBuilder
, um das Recycling in Ihrem CardScrollAdapter
zu implementieren, wie im folgenden Beispiel:
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);
}
Stabile Karten-IDs implementieren
Wenn eine Karte ausgewählt ist und Nutzern angezeigt wird, möchten Sie möglicherweise nicht, dass Änderungen am zugrunde liegenden Adapter die Karte beeinflussen, die die Nutzer in diesem Moment sehen. Wenn ein Nutzer beispielsweise eine ausgewählte Karte ansieht und eine Karte links von dieser Karte entfernt wird, kann die vom Nutzer angezeigte Karte möglicherweise nach links verschoben werden, da CardScrollAdapter
standardmäßig IDs dem zugrunde liegenden Datensatz neu zuweist, wenn Änderungen auftreten.
Wenn es logisch erscheint, Ihren Karten eindeutige IDs zuzuweisen, können Sie eine konsistente ID im zugrunde liegenden Datensatz beibehalten, um das oben genannte Problem zu vermeiden.
Überschreiben Sie dazu hasStableIds()
und geben Sie true
zurück. Damit wird dem System mitgeteilt, dass der CardScrollAdapter
bei Änderungen des Datensatzes stabile IDs beibehält. Implementieren Sie außerdem getItemId()
, um die entsprechende eindeutige ID für die Karten in Ihrem Adapter zurückzugeben.
Die Standardimplementierung gibt den Positionsindex der Karte im Adapter zurück, der grundsätzlich instabil ist.
Leerer CardScrollAdapter
Wenn Sie ein leeres Dataset für Adapter haben, wird standardmäßig ein schwarzer Bildschirm angezeigt. Wenn Sie in diesen Fällen eine andere Ansicht darstellen möchten, verwenden Sie setEmptyView()
nicht.
Erstellen Sie stattdessen eine einzelne Karte in Ihrem CardScrollAdapter
.
Horizontales Ziehen-Feedback
Viele integrierte Immersionen in Glass bieten Feedback, wenn nach vorne oder hinten gewischt wird, ohne dass eine Aktion ausgeführt wird. Sie sehen dieses Feedback beispielsweise, wenn Sie nach dem Fotografieren wischen.
Wenn beim Immersion keine horizontalen Wischgesten zum Ausführen anwendungsspezifischer Funktionen verwendet werden, kannst du diesen Zieheffekt erzielen, indem du dein Layout in eine CardScrollView
einfügst, die nur eine Karte enthält.
Kopieren Sie die folgende Hilfsklasse in Ihr Projekt:
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; } } }
Passen Sie die Methode
onCreate
in Ihrer Aktivität so an, dass dieCardScrollView
angezeigt wird, die Ihr Layout enthält.@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // was: setContentView(R.layout.main_activity); setContentView(new TuggableView(this, R.layout.main_activity)); }