Scroller Kartu

Dengan Glass, Anda dapat membuat interaksi yang kaya dengan kartu Anda, seperti men-scroll dan animasi.

Men-scroll kartu dalam aktivitas

Layar dan touchpad Glass sangat bagus untuk menampilkan kartu yang bisa digeser, seperti di linimasa Glass. Jika mem-build aktivitas, Anda dapat membuat jenis efek yang sama dengan widget CardScrollView.

  1. Terapkan CardScrollAdapter untuk menyediakan kartu ke CardScrollView. Anda dapat mem-build hierarki tampilan standar sendiri atau menggunakan class CardBuilder.
  2. Buat CardScrollView yang menggunakan CardScrollAdapter sebagai pemasok untuk kartu.
  3. Tetapkan tampilan konten aktivitas Anda ke CardScrollView atau tampilkan CardScrollView di tata letak.

Berikut adalah implementasi sederhana yang dapat di-scroll melalui tiga kartu:

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

Berinteraksi dengan kartu scroll

Karena CardScrollView memperluas AdapterView Anda dapat mengimplementasikan pemroses Android standar.

  1. Panggil setOnItemClickListener() yang diwarisi di CardScrollView.
  2. Terapkan pengendali onItemClick() untuk peristiwa ketuk.

Berikut adalah ekstensi untuk contoh sebelumnya yang memutar suara ketukan saat Anda mengetuk kartu:

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

Menganimasikan kartu scroll

Ada tiga animasi yang tersedia untuk kartu scroll: Navigasi, Penyisipan, dan Penghapusan.

  1. Mengimplementasikan tindakan penyisipan atau penghapusan pada kartu di posisi yang ditentukan dalam set kartu.
  2. Panggil animate() dan gunakan nilai dari enum CardScrollView.Animation.
  3. Untuk menampilkan animasi yang lebih halus, hapus referensi ke notifyDataSetChanged(). Metode animate() menangani pembaruan tampilan set data.

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

Tips performa dan penerapan untuk kartu scroll

Ingatlah implikasi desain dan performa berikut saat membuat scroller kartu.

Siklus proses kartu

Untuk meningkatkan performa, CardScrollView hanya memuat subset kartu yang disediakan CardScrollAdapter (umumnya, kartu yang dapat dilihat pengguna, dan beberapa lagi). Karena itu, kartu dapat berada dalam salah satu dari empat status umum berikut:

  • Terlepas - Tampilan scroll kartu tidak memerlukan kartu saat ini. Anda akan diberi tahu oleh metode onDetachedToWindow() kartu jika kartu sebelumnya terpasang lalu dilepaskan.
  • Terpasang - Tampilan scroll kartu meminta kartu dari adaptor dengan getView(), karena kartu tersebut hampir "diaktifkan". Anda akan diberi tahu oleh metode onAttachedToWindow() kartu saat hal ini terjadi.
  • Diaktifkan - Kartu dapat dilihat sebagian oleh pengguna, tetapi tampilan scroll kartu belum "memilih" kartu untuk ditampilkan kepada pengguna. Metode 'isActivated()' menampilkan true dalam kasus ini.
  • Dipilih - Kartu tersebut memenuhi seluruh layar pengguna. Memanggil getSelectedView() akan menampilkan kartu yang saat ini dipilih. Dalam hal ini, metode isSelected() akan menampilkan true.

Jika Anda menganimasikan tampilan kartu atau melakukan operasi mahal lainnya, mulai dan hentikan operasi di onAttachedToWindow() dan onDetachedToWindow() untuk menghemat resource.

Daur ulang kartu

Saat kartu dilepas dari dilepas menjadi dilepas, objek tampilan yang terkait dengan kartu tersebut dapat didaur ulang dan digunakan oleh kartu yang sedang terpasang. Daur ulang tampilan dengan informasi yang diperbarui jauh lebih efisien daripada pembuatan tampilan baru.

Untuk memanfaatkan pendaurulangan kartu, implementasikan metode getItemViewType(), getViewTypeCount(), dan getView() dari class CardScrollAdapter. Kemudian, Anda menggunakan beberapa metode praktis di class CardBuilder untuk mengimplementasikan daur ulang di CardScrollAdapter, seperti dalam contoh berikut:

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

Menerapkan ID kartu stabil

Saat kartu dipilih dan ditampilkan kepada pengguna, Anda mungkin tidak ingin perubahan pada adaptor dasar memengaruhi kartu yang dilihat pengguna pada saat itu. Misalnya, jika pengguna melihat kartu yang dipilih, dan kartu dihapus di sebelah kiri kartu tersebut, kartu yang dilihat pengguna berpotensi dipindahkan ke kiri, karena CardScrollAdapter menetapkan kembali ID ke set data pokok saat perubahan terjadi, secara default.

Jika secara logis untuk menetapkan ID unik kartu, Anda dapat mempertahankan ID yang konsisten pada set data pokok untuk mencegah masalah yang disebutkan di atas. Untuk melakukannya, ganti hasStableIds() dan tampilkan true. Ini akan menentukan sistem bahwa CardScrollAdapter mempertahankan ID stabil di seluruh perubahan set data. Selain itu, terapkan getItemId() untuk menampilkan ID unik yang sesuai untuk kartu di adaptor Anda. Implementasi default menampilkan indeks posisi kartu dalam adaptor, yang pada dasarnya tidak stabil.

CardScrollAdapter Kosong

Jika Anda memiliki set data kosong untuk adaptor, tampilan default-nya adalah menampilkan layar hitam. Jika Anda ingin menampilkan tampilan yang berbeda dalam kasus ini, jangan gunakan setEmptyView(). Sebagai gantinya, buat satu kartu di CardScrollAdapter.

Masukan tarikan horizontal

Banyak imersif imersif pada Glass memberikan masukan "menarik" saat menggeser ke belakang dan ke depan tidak akan melakukan tindakan. Misalnya, Anda dapat melihat masukan ini saat menggeser setelah mengambil foto.

Jika imersif tidak menggunakan gestur geser horizontal untuk melakukan fungsi khusus aplikasi, berikan efek tarik ini dengan menggabungkan tata letak Anda di dalam CardScrollView yang berisi satu kartu.

  1. Salin class helper berikut ke dalam project Anda:

    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. Ubah metode onCreate dalam aktivitas Anda untuk menampilkan CardScrollView yang berisi tata letak.

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