Với Glass, bạn có thể tạo ra các tương tác đa dạng thức với các thẻ như cuộn và ảnh động.
Thẻ cuộn trong các 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ể vuốt, như trong dòng thời gian Google Glass. Nếu đang tạo một hoạt động, bạn có thể tạo cùng một loại hiệu ứng bằng tiện ích CardScrollView
.
- Triển khai
CardScrollAdapter
để cung cấp thẻ choCardScrollView
. Bạn có thể tự xây dựng hệ phân cấp thành phần hiển thị chuẩn hoặc sử dụng lớpCardBuilder
. - Tạo
CardScrollView
sử dụngCardScrollAdapter
làm nhà cung cấp thẻ. - Đặt thành phần hiển thị nội dung của hoạt động là
CardScrollView
hoặc hiển thịCardScrollView
trong bố cục.
Dưới đây là một cách triển khai đơn giản có thể cuộn qua ba 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
Vì CardScrollView
mở rộng AdapterView
, bạn có thể triển khai trình nghe Android tiêu chuẩn.
- Gọi
setOnItemClickListener()
kế thừa trênCardScrollView
của bạn. - Triển khai trình xử lý
onItemClick()
cho sự kiện nhấn.
Đây là phần mở rộng cho ví dụ trước phát âm thanh nhấn khi bạn nhấn vào 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 cho thẻ cuộn: Điều hướng, Chèn và Xoá.
- Triển khai thao tác chèn hoặc xoá trên thẻ tại một vị trí đã chỉ định trong nhóm thẻ.
- Gọi
animate()
và sử dụng một giá trị từ enumCardScrollView.Animation
. Để ảnh động xuất hiện mượt mà hơn, hãy xoá mọi tệp tham chiếu đến
notifyDataSetChanged()
. Phương thứcanimate()
sẽ xử lý việc cập nhật chế độ xem tập dữ liệu của bạn.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 triển khai và hiệu suất cho 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 thẻ
Để tăng hiệu suất, CardScrollView
chỉ tải một tập hợp con của các thẻ mà CardScrollAdapter
cung cấp (thường là các thẻ mà người dùng nhìn thấy và một vài thẻ khác).
Do đó, một thẻ có thể rơi vào bất kỳ trạng thái chung nào sau đây:
- Tách biệt – Chế độ xem cuộn thẻ hiện không cần thẻ này.
Bạn sẽ nhận được thông báo bằng phương thức
onDetachedToWindow()
của thẻ nếu trước đó một thẻ đã được đính kèm và sau đó tách ra. - Đính kèm – Chế độ xem cuộn thẻ yêu cầu thẻ từ bộ chuyển đổi với
getView()
, vì thẻ này sắp được "kích hoạt". Bạn sẽ nhận được thông báo bằng phương thứconAttachedToWindow()
của thẻ khi việc này xảy ra. - Đã kích hoạt – Thẻ mà người dùng nhìn thấy một phần, nhưng chế độ xem cuộn thẻ chưa "chọn" thẻ để hiển thị cho người dùng. Phương thức 'isActivated()' sẽ trả về
true
trong trường hợp này. - Đã chọn – Thẻ đang chiếm toàn bộ màn hình của người dùng. Việc gọi
getSelectedView()
sẽ trả về thẻ hiện được chọn. Phương thứcisSelected()
sẽ trả về giá trị đúng trong trường hợp này.
Nếu bạn đang tạo ảnh động cho chế độ xem thẻ của mình hoặc thực hiện các thao tác tốn kém khác, hãy bắt đầu và dừng các thao tác trong onAttachedToWindow()
và onDetachedToWindow()
để tiết kiệm tài nguyên.
Tái chế thẻ
Khi một thẻ được chuyển sang chế độ tách rời, đối tượng chế độ xem liên kết với thẻ này có thể được tái chế và sử dụng trong một thẻ đang được đính kèm. Việc khôi phục chế độ xem bằng thông tin cập nhật sẽ hiệu quả hơn nhiều so với việc tạo chế độ xem mới.
Để tận dụng tính năng tái chế thẻ, hãy triển khai getItemViewType()
, getViewTypeCount()
và các phương thức getView()
của lớp CardScrollAdapter
. Sau đó, bạn có thể sử dụng một số phương thức thuận tiện trong lớp 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 ảnh hưở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 thẻ đã chọn và thẻ bị xoá ở bên trái thẻ đó thì thẻ mà người dùng đang xem có thể 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.
Nếu việc gán các mã nhận dạng duy nhất cho thẻ của bạn là hợp lý, bạn có thể duy trì
mã nhận dạng nhất quán trong tập dữ liệu cơ bản để ngăn ngừa vấn đề 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ỉ định cho hệ thống rằng CardScrollAdapter
duy trì mã nhận dạng ổn định trong các thay đổi đối với tập dữ liệu. Ngoài ra, hãy triển khai
getItemId()
để trả về mã nhận dạng duy nhất thích hợp cho các thẻ trong bộ chuyển đổi của bạn.
Cách triển khai mặc định sẽ trả về chỉ mục vị trí của thẻ trong bộ chuyển đổi, vốn vốn không ổn định.
CardScrollAdapter trống
Khi bạn có một 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 màu đen. Nếu bạn muốn hiển thị một thành phần hiển thị 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 kéo theo chiều ngang
Nhiều trò chơi tích hợp sẵn trên Glass cung cấp phản hồi "kéo" khi vuốt ngược và không thực hiện được thao tác nào. Ví dụ: bạn có thể thấy phản hồi này khi vuốt sau khi chụp ảnh.
Nếu video nhúng của bạn không sử dụng 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 CardScrollView
chứa một thẻ.
Sao chép lớp trợ giúp sau vào dự á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; } } }
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.@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // was: setContentView(R.layout.main_activity); setContentView(new TuggableView(this, R.layout.main_activity)); }