
استمرارًا للمقالة السابقة ، سأتحدث في هذا الموضوع عن ItemDecoration
و ItemAnimator
وأحاول شرح مبدأ عملهم في RecyclerView
باستخدام مثال لتطبيق بسيط متاح على Github .
1. Item زخرفة
يستخدم ItemDecoration
لتزيين عناصر القائمة في RecyclerView
.
باستخدام ItemDecoration
يمكنك إضافة فواصل بين مكونات view
أو محاذاتها أو تقسيمها على فترات متساوية. لإضافة فاصل بسيط بين مكونات view
، استخدم فئة DividerItemDecoration
، والتي يمكن العثور عليها في إصدار مكتبة الدعم 25.1.0 والإصدارات الأحدث. يوضح جزء التعليمات البرمجية التالي تنفيذه:
mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), mLayoutManager.getOrientation()); recyclerView.addItemDecoration(mDividerItemDecoration);
أفضل طريقة لإنشاء الفاصل الخاص بك هو توسيع فئة RecyclerView.ItemDecoration
. في تطبيق العينة ، استخدمت GridLayoutManager
وقمت بتطبيق CharacterItemDecoration
على RecyclerView
:
recyclerView.addItemDecoration(new CharacterItemDecoration(50));
هنا يقوم CharacterItemDecoration
بتعيين الإزاحة ( eng. offset) إلى 50 بكسل في getItemOffsets(...)
وإلغاء getItemOffsets(...)
. داخل طريقة getItemOffsets()
، outRects
كل حقل outRects
عدد وحدات البكسل التي يجب تعيينها لكل مكون view
، على غرار الهوامش الداخلية والخارجية. نظرًا لأنني استخدمت GridLayoutManager
وأردت تعيين مسافات متساوية بين عناصر الشبكة ، فقد قمت بتعيين المسافة البادئة على اليمين إلى 25 بكسل (أي الإزاحة / 2) لكل عنصر زوجي والمسافة البادئة على اليسار إلى 25 بكسل لكل عنصر فردي ، مع الحفاظ على نفس المسافة البادئة نفسها لجميع العناصر.

2. ItemAnimator
يستخدم ItemAnimator
لتحريك العناصر أو view
المكونات داخل RecyclerView
.

دعونا نجعل تطبيقنا يشبه Instagram من خلال توسيع DefaultItemAnimator
وتجاوز عدة طرق.
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) { return true; }
تحدد طريقة canReuseUpdatedViewHolder(...)
ما إذا كان ViewHolder
استخدام ViewHolder
نفسه للرسوم المتحركة إذا تغيرت بيانات هذا العنصر. إذا ViewHolders
false
، فسيتم ViewHolders
كل من ViewHolders
- القديمة والمحدثة - إلى أسلوب animateChange(...)
.
public ItemHolderInfo recordPreLayoutInformation(@NonNull RecyclerView.State state, @NonNull RecyclerView.ViewHolder viewHolder, int changeFlags, @NonNull List<Object> payloads) { if (changeFlags == FLAG_CHANGED) { for (Object payload : payloads) { if (payload instanceof String) { return new CharacterItemHolderInfo((String) payload); } } } return super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads); } public static class CharacterItemHolderInfo extends ItemHolderInfo { public String updateAction; public CharacterItemHolderInfo(String updateAction) { this.updateAction = updateAction; } }
RecyclerView
يستدعي طريقة recordPreLayoutInformation(...)
لبدء تقديم layout
. يجب أن يقوم ItemAnimator
بتسجيل المعلومات الضرورية حول مكون view
قبل استبداله أو نقله أو حذفه. سيتم نقل البيانات التي يتم إرجاعها بواسطة هذه الطريقة إلى طريقة الرسوم المتحركة المقابلة (في حالتنا ، هذا هو animateChange(...)
).
@Override public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder, @NonNull RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) { if (preInfo instanceof CharacterItemHolderInfo) { CharacterItemHolderInfo recipesItemHolderInfo = (CharacterItemHolderInfo) preInfo; CharacterRVAdapter.CharacterViewHolder holder = (CharacterRVAdapter.CharacterViewHolder) newHolder; if (CharacterRVAdapter.ACTION_LIKE_IMAGE_DOUBLE_CLICKED.equals(recipesItemHolderInfo.updateAction)) { animatePhotoLike(holder); } } return false; } private void animatePhotoLike(final CharacterRVAdapter.CharacterViewHolder holder) { holder.likeIV.setVisibility(View.VISIBLE); holder.likeIV.setScaleY(0.0f); holder.likeIV.setScaleX(0.0f); AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator scaleLikeIcon = ObjectAnimator.ofPropertyValuesHolder (holder.likeIV, PropertyValuesHolder.ofFloat("scaleX", 0.0f, 2.0f), PropertyValuesHolder.ofFloat("scaleY", 0.0f, 2.0f), PropertyValuesHolder.ofFloat("alpha", 0.0f, 1.0f, 0.0f)); scaleLikeIcon.setInterpolator(DECELERATE_INTERPOLATOR); scaleLikeIcon.setDuration(1000); ObjectAnimator scaleLikeBackground = ObjectAnimator.ofPropertyValuesHolder (holder.characterCV, PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.95f, 1.0f), PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.95f, 1.0f)); scaleLikeBackground.setInterpolator(DECELERATE_INTERPOLATOR); scaleLikeBackground.setDuration(600); animatorSet.playTogether(scaleLikeIcon, scaleLikeBackground); animatorSet.start(); }
يستدعي RecyclerView
أسلوب animateChange(...)
عند وجود عنصر المحول قبل وبعد التقديم بعد استدعاء الأسلوب notifyItemChanged(int)
. يمكن استخدام هذه الطريقة أيضًا عند استدعاء notifyDataSetChanged()
، إذا كان المحول يستخدم معرفات ثابتة. يعد ذلك ضروريًا بحيث يمكن لـ RecyclerView
إعادة استخدام مكونات view
في ViewHolders
نفسها. لاحظ أن هذه الطريقة تأخذ كوسيطة: (ViewHolder oldHolder ، ViewHolder newHolder ، ItemHolderInfo preInfo ، ItemHolderInfo postInfo) . نظرًا لأننا نعيد استخدام ViewHolder
، فإن كل من oldHolder و newHolder متماثلان.
عندما ينقر المستخدم نقرًا مزدوجًا فوق أي عنصر ، تُسمى الطريقة التالية:
notifyItemChanged(position, ACTION_LIKE_IMAGE_DOUBLE_CLICKED);
يؤدي هذا إلى بدء السلسلة الكاملة من المكالمات: canReuseUpdatedViewHolder(...)
و recordPreLayoutInformation(...)
، وفي نهاية المطاف ، animateChange(...)
في ItemAnimator
، والذي بدوره يحرك عنصر القائمة وأيقونة القلب في هذا العنصر ( مثال في ملف GIF أعلاه).
هذا هو الجزء الثاني من سلسلة مقالات حول RecyclerView
. إذا فاتك الجزء الأول ، فاقرأه هنا .
بعض المقالات الجيدة على RecyclerView
:
← نصائح للاستخدام المهني RecyclerView. الجزء الأول