Scorritore schede

Con Glass puoi creare interazioni multimediali con le schede, ad esempio scorrimento e animazioni.

Scorrimento delle schede nelle attività

Il display e il touchpad di Glass sono ottimi per visualizzare schede scambiabili, ad esempio nella sequenza temporale di Glass. Se stai creando un'attività, puoi creare lo stesso tipo di effetto con il widget CardScrollView.

  1. Implementa una CardScrollAdapter per fornire schede a CardScrollView. Puoi creare personalmente una gerarchia di viste standard o utilizzare la classe CardBuilder.
  2. Creare una CardScrollView che utilizzi CardScrollAdapter come fornitore per le schede.
  3. Imposta la visualizzazione dei contenuti dell'attività come CardScrollView o visualizza CardScrollView in un layout.

Ecco una semplice implementazione che scorre tre schede:

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);
        }
    }
}

Interazione con le schede a scorrimento

Poiché CardScrollView si estende AdapterView, puoi implementare gli ascoltatori Android standard.

  1. Chiama il metodo setOnItemClickListener() ereditato sul tuo CardScrollView.
  2. Implementare un gestore onItemClick() per l'evento tocco.

Ecco un'estensione dell'esempio precedente che riproduce il suono di un tocco quando tocchi una scheda:

    @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);
            }
        });
    }

Animazione delle schede a scorrimento

Sono disponibili tre animazioni per le schede a scorrimento: Navigazione, Inserimento ed Eliminazione.

  1. Implementa un'azione di inserimento o eliminazione su una scheda in una posizione specifica all'interno dell'insieme di schede.
  2. Chiama animate() e utilizza un valore dell'enumerazione CardScrollView.Animation.
  3. Per visualizzare un'animazione più fluida, rimuovi eventuali riferimenti a notifyDataSetChanged(). Il metodo animate() gestisce l'aggiornamento della visualizzazione del set di dati.

    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);
    }
    

Suggerimenti per il rendimento e l'implementazione delle schede a scorrimento

Durante la creazione dei dispositivi di scorrimento delle schede, tieni presente le seguenti implicazioni in termini di progettazione e prestazioni.

Ciclo di vita di una scheda

Per migliorare le prestazioni, CardScrollView carica solo un sottoinsieme di schede fornite da CardScrollAdapter (in genere, quelle visibili all'utente e altre ancora). Per questo motivo, una scheda può trovarsi in uno dei seguenti quattro stati generali:

  • Scollegata: la visualizzazione a scorrimento della scheda non è necessaria al momento. Se una scheda è stata collegata e poi scollegata, ti viene inviata una notifica in base al metodo onDetachedToWindow().
  • Allegata: la visualizzazione a scorrimento della scheda richiede la scheda all'adattatore con getView(), perché sta per essere "attivata". Quando ciò accade, ti viene inviata una notifica con il metodo onAttachedToWindow() della carta.
  • Attivata: la scheda è parzialmente visibile all'utente, ma la visualizzazione a scorrimento non ha "selezionato" la scheda da mostrare all'utente. In questo caso, il metodo 'isActivated()' restituisce true.
  • Selezionata. La scheda occupa l'intero schermo dell'utente. La chiamata a getSelectedView() restituisce la carta attualmente selezionata. In questo caso, il metodo isSelected() restituisce true.

Se vuoi animare la visualizzazione della scheda o eseguire altre operazioni costose, avvia e interrompi le operazioni in onAttachedToWindow() e onDetachedToWindow() per risparmiare risorse.

Riciclo delle carte

Quando una scheda va da collegata a scollegata, l'oggetto vista associato può essere riciclato e utilizzato da una scheda che viene allegata. Riciclare le visualizzazioni con informazioni aggiornate è molto più efficiente rispetto alla creazione di nuove viste.

Per sfruttare il riciclo delle carte, implementa i metodi getItemViewType(), getViewTypeCount() e getView() della classe CardScrollAdapter. Puoi quindi utilizzare alcuni dei metodi disponibili nella classe CardBuilder per implementare il riciclo nella CardScrollAdapter, come nell'esempio seguente:

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);
}

Implementare gli ID scheda stabili

Quando una scheda viene selezionata e mostrata agli utenti, potresti non voler modificare l'adattatore sottostante per influire sulla scheda che gli utenti vedono in quel momento. Ad esempio, se un utente sta visualizzando una scheda selezionata e questa viene rimossa alla sua sinistra, la scheda che sta visualizzando potrebbe essere spostata a sinistra perché, per impostazione predefinita, CardScrollAdapter riassegna gli ID al set di dati sottostante quando si verificano modifiche.

Se ha senso assegnare in modo logico ID univoci alle tue carte, puoi mantenere un ID coerente nel set di dati sottostante per evitare il problema indicato in precedenza. Per farlo, sostituisci hasStableIds() e restituisci true. Specifica al sistema che CardScrollAdapter mantiene gli ID stabili tra le modifiche del set di dati. Inoltre, implementa getItemId() per restituire l'ID univoco appropriato per le carte nel tuo adattatore. L'implementazione predefinita restituisce l'indice di posizione della scheda nell'adattatore, che è intrinsecamente instabile.

Adattatore Cardscroll vuoto

Se hai un set di dati vuoto per gli adattatori, la visualizzazione predefinita mostra una schermata nera. In questi casi, se vuoi mostrare una visualizzazione diversa, non utilizzare setEmptyView(). Crea invece una singola tessera nella CardScrollAdapter.

Feedback sul tiro orizzontale

Molte rapide operazioni di immersione integrate in Glass forniscono un feedback "agganciante" quando scorri indietro e in avanti non eseguono un'azione. Ad esempio, puoi vedere questo feedback quando scorri dopo aver scattato una foto.

Se la tua immersione non utilizza i gesti di scorrimento orizzontale per eseguire funzioni specifiche per l'applicazione, fornisci questo effetto di ritaglio inserendo il layout in una CardScrollView che contiene una scheda.

  1. Copia il seguente corso helper nel tuo progetto:

    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;
            }
        }
    }
    
  2. Modifica il metodo onCreate nella tua attività per visualizzare il CardScrollView che contiene il layout.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        // was: setContentView(R.layout.main_activity);
        setContentView(new TuggableView(this, R.layout.main_activity));
    }