Trình cuộn thẻ

Với Glass, bạn có thể tạo nhiều tương tác phong phú với thẻ của mình, chẳng hạn như cuộn và ảnh động.

Cuộn thẻ trong hoạt động

Màn hình Glass và bàn di chuột rất phù hợp để hiển thị các thẻ có thể lướt, như trong dòng thời gian của Google Glass. Nếu đang tạo một hoạt động, bạn có thể tạo có cùng loại hiệu ứng với CardScrollView tiện ích.

  1. Triển khai một CardScrollAdapter để cung cấp thẻ cho CardScrollView. Bạn có thể tự xây dựng một hệ phân cấp khung hiển thị chuẩn hoặc sử dụng CardBuilder .
  2. Tạo một CardScrollView sử dụng CardScrollAdapter nhà cung cấp thẻ.
  3. Đặt chế độ xem nội dung của hoạt động thành CardScrollView hoặc hiển thị CardScrollView trong một bố cục.

Dưới đây là cách triển khai đơn giản bằng cách cuộn qua 3 thẻ:

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

Tương tác với thẻ cuộn

Từ CardScrollView mở rộng AdapterView bạn có thể triển khai trình nghe Android chuẩn.

  1. Gọi hàm kế thừa setOnItemClickListener() trên CardScrollView của bạn.
  2. Triển khai một onItemClick() trình xử lý cho sự kiện nhấn.

Đây là một phần mở rộng của ví dụ trước có chức năng phát âm thanh khi nhấn khi bạn nhấn vào một thẻ:

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

Tạo ảnh động cho thẻ cuộn

Có 3 ảnh động có sẵn để cuộn thẻ: Điều hướng, Chèn và Xoá.

  1. Thực hiện thao tác chèn hoặc xoá đối với thẻ tại vị trí được chỉ định trong bộ thẻ.
  2. Gọi điện animate() và sử dụng một giá trị từ enum CardScrollView.Animation.
  3. Để ảnh động xuất hiện mượt mà hơn, hãy xoá mọi tham chiếu đến notifyDataSetChanged(). animate() xử lý việc cập nhật chế độ xem tập dữ liệu.

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

Mẹo về hiệu suất và triển khai thẻ cuộn

Hãy lưu ý những ảnh hưởng sau đây về thiết kế và hiệu suất khi tạo trình cuộn thẻ.

Vòng đời của thẻ

Để tăng hiệu suất, CardScrollView Chỉ tải một nhóm nhỏ thẻ mà CardScrollAdapter cung cấp (thường là những tiện ích bổ sung mà người dùng nhìn thấy và một vài tiện ích khác). Do đó, thẻ có thể ở một trong 4 trạng thái chung sau đây:

  • Tách riêng – Chế độ xem cuộn thẻ không cần thẻ này tại thời điểm hiện tại. Bạn sẽ nhận được thông báo từ onDetachedToWindow() của thẻ nếu thẻ đã được đính kèm trước đó và sau đó được tách ra.
  • Đã đính kèm – Chế độ xem cuộn thẻ yêu cầu thẻ từ bộ chuyển đổi bằng getView(), vì thẻ sắp được "kích hoạt". Bạn sẽ nhận được thông báo bằng phương thức onAttachedToWindow() của thẻ khi điều này xảy ra.
  • Đã kích hoạt – Người dùng chỉ nhìn thấy một phần thẻ, nhưng thẻ thì cuộn chưa "chọn" chế độ xem thẻ hiển thị cho người dùng. Chiến lược phát hành đĩa đơn Phương thức 'isKích hoạt()' trả về true trong trường hợp này.
  • Đã chọn - Thẻ đang chiếm toàn bộ màn hình. Nếu gọi getSelectedView(), bạn sẽ trả về thẻ hiện được chọn. Phương thức isSelected() trả về true trong trường hợp này.

Nếu bạn đang tạo ảnh động cho chế độ xem thẻ hoặc thực hiện các thao tác tốn kém khác, bắt đầu và dừng hoạt động trong onAttachedToWindow()onDetachedToWindow() để tiết kiệm tài nguyên.

Tái chế thẻ

Khi thẻ chuyển từ trạng thái được đính kèm sang trạng thái tách ra, đối tượng chế độ xem sẽ liên kết với Thẻ có thể được tái chế và sử dụng cho thẻ đang được gắn. Việc tái chế các thành phần hiển thị với thông tin cập nhật sẽ hiệu quả hơn nhiều so với tạo chế độ xem mới.

Để tận dụng việc tái chế thẻ, hãy triển khai getItemViewType()! getViewTypeCount(), và getView() của CardScrollAdapter . Sau đó, bạn có thể sử dụng một số phương thức tiện lợi trong CardBuilder để triển khai việc tái chế trong CardScrollAdapter, như trong ví dụ sau:

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

Triển khai mã thẻ ổn định

Khi một thẻ được chọn và hiển thị cho người dùng, bạn có thể không muốn các thay đổi đối với bộ chuyển đổi cơ bản tác động đến thẻ mà người dùng thấy tại thời điểm đó. Ví dụ: nếu người dùng đang xem một thẻ đã chọn và một thẻ bị xoá ở bên trái của thẻ đó, là thẻ mà người dùng đang xem có thể di chuyển sang trái vì CardScrollAdapter gán lại mã nhận dạng cho tập dữ liệu cơ bản khi có thay đổi theo mặc định.

Nếu việc chỉ định mã nhận dạng duy nhất cho thẻ của mình là hợp lý, bạn có thể duy trì một mã nhận dạng nhất quán trong tập dữ liệu cơ bản để ngăn chặn sự cố nêu trên. Để thực hiện việc này, hãy ghi đè hasStableIds() và trả về true. Điều này chỉ rõ cho hệ thống rằng CardScrollAdapter duy trì mã nhận dạng ổn định qua các thay đổi của tập dữ liệu. Ngoài ra, hãy triển khai getItemId() để trả về mã nhận dạng duy nhất phù hợp cho các thẻ trong bộ chuyển đổi. Phương thức triển khai mặc định sẽ trả về chỉ mục vị trí của thẻ trong bộ chuyển đổi, vốn không ổn định.

CardScrollAdapter trống

Khi bạn có tập dữ liệu trống cho các bộ chuyển đổi, chế độ xem mặc định sẽ hiển thị màn hình đen. Nếu bạn muốn hiển thị một chế độ xem khác trong những trường hợp này, không sử dụng setEmptyView(). Thay vào đó, hãy tạo một thẻ duy nhất trong CardScrollAdapter.

Phản hồi về tính năng kéo theo chiều ngang

Nhiều chế độ nhập vai tích hợp trên Glass mang đến khả năng "kéo" khi vuốt ngược lại. thì không thực hiện hành động. Ví dụ: bạn có thể thấy phản hồi này khi vuốt sau khi chụp ảnh.

Nếu bạn không thực hiện được các cử chỉ vuốt ngang để thực hiện, các hàm dành riêng cho ứng dụng, hãy cung cấp hiệu ứng kéo này bằng cách gói bố cục bên trong một CardScrollView chứa một thẻ.

  1. Sao chép lớp trợ giúp sau đây vào dự án của bạn:

    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. Sửa đổi phương thức onCreate trong hoạt động của bạn để hiển thị CardScrollView chứa bố cục của bạn.

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