Przewijanie kart

Google Glass pozwala na tworzenie zaawansowanych interakcji z kartami, np. z przewijania i animacje.

Przewijanie kart w aktywnościach

Wyświetlacz i touchpad Glass świetnie nadają się do wyświetlania przesuwanych kart, jak w przypadku osi czasu w Google Glass. Jeśli tworzysz aktywność, możesz taki sam efekt w przypadku CardScrollView widżet.

  1. Zaimplementuj CardScrollAdapter aby zaopatrzyć się w karty CardScrollView Standardową hierarchię widoków możesz utworzyć samodzielnie lub za pomocą CardBuilder zajęcia.
  2. Utwórz CardScrollView który wykorzystuje CardScrollAdapter dostawcy kart.
  3. Ustaw widok treści aktywności na CardScrollView lub wyświetl CardScrollView w układzie.

Oto prosta implementacja, która wyświetla się po 3 kartach:

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

Interakcja z przewijanymi kartami

Od CardScrollView obejmuje AdapterView możesz zastosować standardowe detektory na Androida.

  1. Wywołaj metodę dziedziczone setOnItemClickListener() na urządzeniu CardScrollView.
  2. Zaimplementuj tag onItemClick() dla zdarzenia dotknięcia.

Oto rozszerzenie poprzedniego przykładu, w którym słychać dźwięk dotknięcia po dotknięciu karty:

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

Animowanie przewijanych kart

W przypadku kart przewijanych dostępne są 3 animacje: Nawigacja, Wstawianie i Usuwanie.

  1. Zaimplementuj funkcję wstawiania lub usuwania na karcie w określonym miejscu zestawu kart.
  2. Zadzwoń do nas animate() i użyj wartości z wyliczenia CardScrollView.Animation.
  3. Aby zapewnić płynniejszą animację, usuń wszelkie odniesienia do notifyDataSetChanged() animate() obsługuje aktualizowanie widoku zbioru danych.

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

Wskazówki dotyczące skuteczności i implementacji przewijanych kart

Pamiętaj o poniższych wpływach na wygląd i wydajność przewijających karty.

Cykl życia karty

Aby zwiększyć skuteczność, CardScrollView wczytuje tylko podzbiór kart, które są CardScrollAdapter udostępnia (zwykle te, które są widoczne dla użytkownika i kilka innych). Z tego powodu karta może mieć jeden z 4 ogólnych stanów:

  • Odłączona – widok przewijania karty nie potrzebuje jej w danej chwili. Otrzymasz powiadomienie onDetachedToWindow() karty jeśli karta została wcześniej podłączona, a następnie odłączona.
  • Dołączono – widok przewijania karty żąda karty od adaptera z parametrem getView(), ponieważ karta jest bliska aktywacji. Gdy tak się stanie, otrzymasz powiadomienie przy użyciu metody onAttachedToWindow() karty.
  • aktywowana – karta jest częściowo widoczna dla użytkownika, ale przewija się; widok danych nie został „wybrany” kartę wyświetlaną użytkownikowi. Metoda 'isActivated()' w tym przypadku zwraca true.
  • Wybrane – karta zajmuje całego ekranu. Połączenie z numerem getSelectedView() zwróci aktualnie wybraną kartę. Metoda isSelected() zwraca w tym przypadku.

Jeśli animujesz widok karty lub wykonujesz inne kosztowne operacje, rozpocznij i zatrzymaj operacje w onAttachedToWindow() oraz onDetachedToWindow() aby oszczędzać zasoby.

Recykling kart

Gdy karta zostaje odłączona, obiekt widoku danych powiązany z że karta może zostać poddana recyklingowi i służy do użycia z dołączoną kartą. Widoki recyklingu ze zaktualizowanymi informacjami są znacznie efektywniejsze niż tworzenie nowych widoków.

Aby wykorzystać możliwości recyklingu kart, zaimplementuj getItemViewType() getViewTypeCount(), i getView() CardScrollAdapter zajęcia. Następnie korzystasz z udogodnień opisanych w artykule CardBuilder. na temat wdrożenia recyklingu w: CardScrollAdapter, jak w tym przykładzie:

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

Wdrażanie stabilnych identyfikatorów kart

Po wybraniu karty i wyświetlaniu jej użytkownikom możesz nie mieć możliwości chcesz, aby zmiany na podstawie adaptera wpłynęły na kartę widoczną dla użytkowników w danym momencie. Jeśli na przykład użytkownik wyświetla wybraną kartę, jest usuwana z lewej strony karty, może przesunąć się w lewo, ponieważ CardScrollAdapter domyślnie powoduje zmianę identyfikatorów do bazowego zbioru danych po wystąpieniu zmian.

Jeśli przypisanie unikalnych identyfikatorów ma sens, możesz zachować spójny identyfikator w bazowym zbiorze danych, by zapobiec temu problemowi. Aby to zrobić, zastąp hasStableIds() i zwróć true. Określa to systemowi, że CardScrollAdapter zachowuje stabilne identyfikatory niezależnie od zmian w zbiorze danych. Dodatkowo zaimplementuj getItemId() , by zwracały odpowiedni, unikalny identyfikator kart w adapterze. Domyślna implementacja zwraca indeks pozycji karty w adapterze, co jest z natury niestabilne.

Pusta karta przewinięcia karty

Jeśli masz pusty zbiór danych adapterów, domyślnie wyświetlany jest widok pojawi się czarny ekran. Jeśli w takim przypadku chcesz wyświetlić inny widok, nie używaj setEmptyView(). Zamiast tego utwórz jedną kartę w CardScrollAdapter.

Informacje o przeciąganiu w poziomie

Wiele wbudowanych zagłębień w Google Glass umożliwia „ciągnięcie” po przesunięciu palcem do tyłu nie wykonują żadnej czynności. Na przykład możesz zobaczyć tę informację, gdy przesuń palcem po zrobieniu zdjęcia.

Jeśli urządzenie nie używa gestów przesuwania w poziomie dla konkretnych aplikacji, zapewnij ten efekt przeciągania przez opakowanie układ wewnątrz CardScrollView który zawiera 1 kartę.

  1. Skopiuj do projektu tę klasę pomocniczą:

    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. Zmodyfikuj metodę onCreate w swojej aktywności, aby wyświetlać metodę CardScrollView, która zawiera układ.

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