باستخدام 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()
لحدث النقر.
إليك إضافة إلى المثال السابق تُشغِّل صوت النقر عند النقر على بطاقة:
@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()
إلى عرض البطاقة المحدّدة حاليًا. وتعرض الطريقةisSelected()
القيمة صحيح في هذه الحالة.
إذا كنت تحرّك طريقة عرض بطاقتك أو تُجري عمليات أخرى مكلفة،
يمكنك بدء العمليات وإيقافها في
onAttachedToWindow()
وonDetachedToWindow()
لتوفير الموارد.
إعادة تدوير البطاقات
وعند انتقال بطاقة إلى مرحلة فصلها، يمكن إعادة تدوير عنصر العرض المرتبط بالبطاقة واستخدامه من خلال بطاقة تم إرفاقها. إن إعادة تدوير طرق العرض باستخدام معلومات محدَّثة أكثر فعالية من إنشاء طرق عرض جديدة.
للاستفادة من إعادة تدوير البطاقات، عليك اتّباع طرق
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()
لإرجاع المعرّف الفريد المناسب للبطاقات في المحوّل.
تؤدي عملية التنفيذ التلقائية إلى عرض فهرس موضع البطاقة في المحوّل، وهو أمر غير مستقر بطبيعته.
محوِّل تمرير البطاقة الفارغة
عندما يكون لديك مجموعة بيانات فارغة للمحوّلات، يكون العرض التلقائي هو إظهار شاشة سوداء. إذا أردت إظهار طريقة عرض مختلفة في هذه الحالات،
لا تستخدم السمة 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; } } }
يمكنك تعديل طريقة
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)); }