עם Glass, ניתן ליצור אינטראקציות עשירות עם הכרטיסים, כמו גלילה ואנימציות.
כרטיסים נגללים בפעילויות
מסך ה-Glass ולוח המגע מעולים להצגת כרטיסים מסתובבים,
כמו בציר הזמן של Glass. אם יוצרים פעילות, אפשר ליצור
אותו סוג של אפקט
CardScrollView
לווידג'ט הזה.
- ליישם
CardScrollAdapter
כדי לספק כרטיסיםCardScrollView
אפשר ליצור היררכיית תצוגה רגילה בעצמכם או להשתמשCardBuilder
בכיתה. - לכתוב
CardScrollView
שמשתמשתCardScrollAdapter
כספקית של כרטיסים. - הגדרת תצוגת התוכן של הפעילות
CardScrollView
או להציג אתCardScrollView
בפריסה.
הנה הטמעה פשוטה שגוללת בין שלושה כרטיסים:
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);
}
}
}
אינטראקציה עם כרטיסים נגללים
מאז
CardScrollView
בתוקף למשך AdapterView
אפשר להטמיע את פונקציות ההאזנה הרגילות של Android.
- מפעילים את הפונקציה שעוברת בירושה
setOnItemClickListener()
ב-CardScrollView
. - ליישם
onItemClick()
ה-handler של אירוע ההקשה.
הנה תוספת לדוגמה הקודמת שבה מושמע צליל הקשה כשמקישים על כרטיס:
@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);
}
});
}
אנימציה של כרטיסים נגללים
בכרטיסי גלילה יש שלוש אנימציות: ניווט, הוספה ומחיקה.
- מיישמים פעולת הוספה או מחיקה של כרטיס במיקום ספציפי בקבוצת הכרטיסים.
- שיחת טלפון
animate()
ומשתמשים בערך מהטיפוסCardScrollView.Animation
. כדי להציג אנימציה חלקה יותר, יש להסיר את כל ההפניות אל
notifyDataSetChanged()
animate()
מטפל בעדכון תצוגת קבוצת הנתונים שלך.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); }
טיפים בנושא ביצועים והטמעה של כרטיסים גלילה
חשוב לזכור את ההשלכות הבאות על העיצוב ועל הביצועים כשיוצרים את המשחק לגלילה בכרטיס.
מחזור החיים של הכרטיס
כדי לשפר את הביצועים, CardScrollView
טוען רק חלק מהכרטיסים
CardScrollAdapter
מספקת (בדרך כלל, הנתונים הגלויים למשתמש ועוד כמה).
לכן, כרטיס יכול להיות בכל אחד מארבעת המצבים הכלליים הבאים:
- ניתוק – אין צורך בכרטיס הזה כרגע בתצוגת הגלילה של הכרטיסים.
תוצג לך התראה על כך דרך
onDetachedToWindow()
של הכרטיס. שיטה אם כרטיס צורף בעבר ואז נותק. - מחובר - בתצוגת הגלילה של הכרטיסים דרוש הכרטיס מהמתאם עם
getView()
, כי הכרטיס עומד להיות 'מופעל'. כשזה קורה, מקבלים הודעה על כך באמצעות השיטהonAttachedToWindow()
של הכרטיס. - מופעל – הכרטיס גלוי חלקית למשתמש, אבל הוא נגלל
התצוגה לא 'נבחרה' הכרטיס שיוצג למשתמש.
שיטת 'isActivated() '
במקרה הזה, הפונקציה מחזירה
true
. - נבחר – הכרטיס עובר לחשבון של המשתמש
את כל המסך. התקשרות אל
getSelectedView()
תחזיר את הכרטיס הנוכחי שנבחר. ה-methodisSelected()
מחזירה במקרה הזה.
אם אתם מוסיפים אנימציה לתצוגת הכרטיס או מבצעים פעולות יקרות אחרות,
להתחיל ולהפסיק את הפעולות
onAttachedToWindow()
וגם
onDetachedToWindow()
כדי לחסוך במשאבים.
מיחזור כרטיסים
כשכרטיס עובר מצירוף כרטיס לניתוק, אובייקט ה-View שמשויך אל ניתן למחזר את הכרטיס ולהשתמש בו עם כרטיס שמצורף אליו. תצוגות מיחזור עם מידע עדכני הרבה יותר יעילות מאשר יצירת תצוגות חדשות.
כדי לנצל את היתרונות של מיחזור כרטיסים,
getItemViewType()
getViewTypeCount()
,
ו-getView()
לשיטות של
CardScrollAdapter
בכיתה. לאחר מכן משתמשים בחלק משיטות הנוחות CardBuilder
יכולה לבצע מיחזור בCardScrollAdapter
,
כמו בדוגמה הבאה:
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);
}
הטמעה של מזהי כרטיסים קבועים
כשכרטיס נבחר ומוצג למשתמשים, יכול להיות שלא
אתם רוצים שהשינויים במתאם הבסיסי ישפיעו על הכרטיס שהמשתמשים רואים
באותו רגע. לדוגמה, אם משתמש צופה בכרטיס שנבחר ובכרטיס
מוסר מימין לכרטיס, הכרטיס של המשתמש
עלול לעבור שמאלה, כי
CardScrollAdapter
מקצה מחדש את המזהים למערך הנתונים הבסיסי כאשר מתרחשים שינויים, כברירת מחדל.
אם הגיוני להקצות לכרטיסים מזהים ייחודיים, אפשר
מזהה עקבי בקבוצת הנתונים הבסיסית כדי למנוע את הבעיה שהוזכרה.
כדי לעשות את זה, צריך לשנות את הערך
hasStableIds()
ולהחזיר את הנתונים true
. מציין למערכת
CardScrollAdapter
שומרת מזהים יציבים בכל השינויים בקבוצות הנתונים. בנוסף, מטמיעים
getItemId()
כדי להחזיר את המזהה הייחודי המתאים לכרטיסים במתאם.
הטמעת ברירת המחדל מחזירה את אינדקס המיקום של הכרטיס במתאם,
והיא לא יציבה מטבעה.
כרטיס CardScrollAdapter ריק
אם יש קבוצת נתונים ריקה למתאמים, תצוגת ברירת המחדל היא להציג
מסך שחור. כדי לשנות את התצוגה במקרים כאלה,
אין להשתמש ב-setEmptyView()
.
במקום זאת, צרו כרטיס יחיד בCardScrollAdapter
.
משוב של משיכה אופקית
הרבה מסגרות 'טבולות' ב-Glass מספקות 'משיכה' משוב בזמן החלקה אחורה לא תבצע פעולה. לדוגמה, אתם יכולים לראות את המשוב הזה כאשר החלקה אחרי צילום תמונה.
אם חוויית השחייה לא כוללת תנועות החלקה אופקיות כדי לבצע
ספציפיות לאפליקציה, מספקים את אפקט המשיכה הזה על ידי
פריסה בתוך
CardScrollView
שמכיל כרטיס אחד.
מעתיקים את כיתת העוזרים הבאה לפרויקט:
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; } } }
צריך לשנות את ה-method
onCreate
בפעילות כדי להציג את ה-CardScrollView
מכיל את הפריסה שלכם.@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // was: setContentView(R.layout.main_activity); setContentView(new TuggableView(this, R.layout.main_activity)); }