اسحب وانتقد في RecyclerView. الجزء 2: سحب وإسقاط وحدات التحكم والشبكات والرسوم المتحركة المخصصة

اسحب وانتقد في RecyclerView. الجزء 2: سحب وإسقاط وحدات التحكم والشبكات والرسوم المتحركة المخصصة


في الجزء الأول ، نظرنا إلى ItemTouchHelper وتنفيذ ItemTouchHelper.Callback ، الذي يضيف وظائف السحب والإفلات الأساسية والسحب إلى الفصل إلى RecyclerView . في هذه المقالة ، سنواصل ما تم فعله في المقالة السابقة بإضافة دعم لترتيب العناصر في شكل شبكة ، وسحب وحدات التحكم وإسقاطها ، واختيار عنصر قائمة ، ورسوم متحركة مخصصة للتمرير.


شبكة العناصر


سحب وإسقاط وحدات التحكم


عند إنشاء قائمة تدعم السحب والإفلات ، عادةً ما تنفذ القدرة على سحب العناصر وإفلاتها عن طريق اللمس. يساهم هذا في وضوح القائمة وسهولة استخدامها في "وضع التحرير" ، كما يوصى به من خلال إرشادات المواد. إن إضافة وحدات تحكم بالسحب والإسقاط إلى مثالنا أمر سهل للغاية.


سحب وإسقاط وحدات التحكم


أولاً ، قم بتحديث layout العنصر ( item_main.xml ).


 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/item" android:layout_width="match_parent" android:layout_height="?listPreferredItemHeight" android:clickable="true" android:focusable="true" android:foreground="?selectableItemBackground"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="16dp" android:textAppearance="?android:attr/textAppearanceMedium" /> <ImageView android:id="@+id/handle" android:layout_width="?listPreferredItemHeight" android:layout_height="match_parent" android:layout_gravity="center_vertical|right" android:scaleType="center" android:src="@drawable/ic_reorder_grey_500_24dp" /> </FrameLayout> 

يمكن العثور على الصورة المستخدمة لوحدة التحكم في السحب في أيقونات Material Design وإضافتها إلى المشروع باستخدام المكوّن الإضافي المناسب لإنشاء الرموز في Android Studio.


كما ذكرنا بإيجاز في مقال سابق ، يمكنك استخدام ItemTouchHelper.startDrag(ViewHolder) لبدء السحب والإفلات برمجيًا. لذلك ، كل ما علينا فعله هو تحديث ViewHolder عن طريق إضافة وحدة تحكم بالسحب ، وإعداد معالج لمس بسيط يستدعي startDrag() .


سنحتاج إلى واجهة لنقل الأحداث على طول السلسلة:


 public interface OnStartDragListener { /** * Called when a view is requesting a start of a drag. * * @param viewHolder The holder of the view to drag. */ void onStartDrag(RecyclerView.ViewHolder viewHolder); } 

ثم قم بتحديد ImageView لوحدة تحكم السحب في ItemViewHolder :


 public final ImageView handleView; public ItemViewHolder(View itemView) { super(itemView); // ... handleView = (ImageView) itemView.findViewById(R.id.handle); } 

وتحديث RecyclerListAdapter :


 private final OnStartDragListener mDragStartListener; public RecyclerListAdapter(OnStartDragListener dragStartListener) { mDragStartListener = dragStartListener; // ... } @Override public void onBindViewHolder(final ItemViewHolder holder, int position) { // ... holder.handleView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { mDragStartListener.onStartDrag(holder); } return false; } }); } 

يجب أن يبدو RecyclerListAdapter الآن شيء مثل هذا .


كل ما عليك فعله هو إضافة OnStartDragListener إلى الجزء:


 public class RecyclerListFragment extends Fragment implements OnStartDragListener { // ... @Override public void onViewCreated(View view, Bundle icicle) { super.onViewCreated(view, icicle); RecyclerListAdapter a = new RecyclerListAdapter(this); // ... } @Override public void onStartDrag(RecyclerView.ViewHolder viewHolder) { mItemTouchHelper.startDrag(viewHolder); } } 

يجب أن يبدو RecyclerListFragment الآن مثل هذا . الآن ، عند تشغيل التطبيق ، يمكنك البدء في السحب والإسقاط بلمس وحدة التحكم.


سحب عناصر القائمة


تمييز عنصر قائمة


الآن في مثالنا لا يوجد مؤشر مرئي على العنصر الذي يتم سحبه. من الواضح أن هذا لا ينبغي أن يكون ، ولكن من السهل إصلاحه. باستخدام ItemTouchHelper يمكنك استخدام تأثيرات تمييز العنصر القياسية. على Lollipop والإصدارات الأحدث من Android ، "تنتشر الإضاءة الخلفية" فوق العنصر في عملية التفاعل معه ؛ في الإصدارات السابقة ، يغير العنصر لونه ببساطة إلى اللون الداكن.


لتنفيذ ذلك في مثالنا ، ما FrameLayout سوى إضافة خلفية (خاصية background ) إلى الجذر FrameLayout للعنصر FrameLayout أو تعيينه في مُنشئ RecyclerListAdapter.ItemViewHolder . سيبدو شيء من هذا القبيل:


اختيار العنصر


يبدو رائعًا ، ولكن قد ترغب في التحكم في المزيد. إحدى الطرق للقيام بذلك هي السماح لـ ViewHolder بمعالجة تغييرات الحالة لعنصر. لهذا ، يوفر ItemTouchHelper.Callback إضافيتين:


  • onSelectedChanged(ViewHolder, int) كل مرة تتغير فيها حالة العنصر لسحبه ( ACTION_STATE_DRAG ) أو السحب ( ACTION_STATE_SWIPE ). يعد هذا مكانًا مثاليًا لتغيير حالة مكون view إلى نشط .
  • clearView(RecyclerView, ViewHolder) عند اكتمال مكون view السحب والإفلات ، وكذلك عند اكتمال التمرير السريع ( ACTION_STATE_IDLE ). هنا ، عادة ما يتم استعادة الحالة الأولية لمكون view الخاص بك.

الآن ، دعنا نجمع كل شيء معًا.


أولاً ، قم بإنشاء واجهة سيقوم ViewHolders بتنفيذها:


 /** * Notifies a View Holder of relevant callbacks from * {@link ItemTouchHelper.Callback}. */ public interface ItemTouchHelperViewHolder { /** * Called when the {@link ItemTouchHelper} first registers an * item as being moved or swiped. * Implementations should update the item view to indicate * it's active state. */ void onItemSelected(); /** * Called when the {@link ItemTouchHelper} has completed the * move or swipe, and the active item state should be cleared. */ void onItemClear(); } 

بعد ذلك ، في SimpleItemTouchHelperCallback نفذ الأساليب المناسبة:


 @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { // We only want the active item if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { if (viewHolder instanceof ItemTouchHelperViewHolder) { ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; itemViewHolder.onItemSelected(); } } super.onSelectedChanged(viewHolder, actionState); } @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); if (viewHolder instanceof ItemTouchHelperViewHolder) { ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; itemViewHolder.onItemClear(); } } 

كل ما تبقى الآن هو لـ RecyclerListAdapter.ItemViewHolder لتطبيق ItemTouchHelperViewHolder :


 public class ItemViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder { // ... @Override public void onItemSelected() { itemView.setBackgroundColor(Color.LTGRAY); } @Override public void onItemClear() { itemView.setBackgroundColor(0); } } 

في هذا المثال ، نضيف ببساطة خلفية رمادية أثناء تنشيط العنصر ، ثم نزيله. إذا كان ItemTouchHelper والمحول متصلين بشكل وثيق ، يمكنك بسهولة التخلي عن هذا الإعداد وتبديل حالة مكون view مباشرة في ItemTouchHelper.Callback .


شبكات


إذا حاولت الآن استخدام GridLayoutManager ، فسترى أنه لا يعمل بشكل صحيح. السبب والحل بسيط: يجب أن نخبر ItemTouchHelper بأننا نريد دعم سحب العناصر إلى اليسار واليمين. سابقًا في SimpleItemTouchHelperCallback الذي SimpleItemTouchHelperCallback بالفعل:


 @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; return makeMovementFlags(dragFlags, swipeFlags); } 

التغيير الوحيد المطلوب لدعم الشبكات هو إضافة العلامات المناسبة:


 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; 

ومع ذلك ، فإن التمرير السريع swipeFlags ليس سلوكًا طبيعيًا للغاية لعناصر الشبكة ، لذلك swipeFlags المرجح أن يتم swipeFlags :


 @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; int swipeFlags = 0; return makeMovementFlags(dragFlags, swipeFlags); } 

لمشاهدة مثال GridLayoutManager ، راجع RecyclerGridFragment . إليك ما يبدو عليه عند بدء التشغيل:


عناصر الشبكة


انتقاد الرسوم المتحركة المخصصة


يوفر ItemTouchHelper.Callback طريقة مريحة حقًا للتحكم الكامل في الرسوم المتحركة أثناء السحب أو المسح. نظرًا لأن ItemTouchHelper عبارة عن RecyclerView.ItemDecoration ، يمكننا التدخل في عملية view مكون view بطريقة مماثلة. في الجزء التالي ، سنفحص هذا السؤال بمزيد من التفصيل ، ولكن في الوقت الحالي ، لنلق نظرة على مثال بسيط لتجاوز الرسوم المتحركة الافتراضية للتمرير السريع لإظهار الاختفاء الخطي.


 @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { float width = (float) viewHolder.itemView.getWidth(); float alpha = 1.0f - Math.abs(dX) / width; viewHolder.itemView.setAlpha(alpha); viewHolder.itemView.setTranslationX(dX); } else { super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } } 

dY و dY هي التحول الحالي بالنسبة لمكون view المحدد ، حيث:


  • -1.0f عبارة عن تمريرة كاملة من اليمين إلى اليسار (من ItemTouchHelper.END إلى ItemTouchHelper.START )
  • 1.0f هو التمرير الكامل من اليسار إلى اليمين (من ItemTouchHelper.START إلى ItemTouchHelper.END )

من المهم استدعاء super لأي حالة actionState لا تقوم actionState من أجل تشغيل الرسوم المتحركة الافتراضية.


في الجزء التالي ، سننظر في مثال سنقوم فيه بالتحكم في رسم عنصر في لحظة السحب والإفلات.


الخلاصة


في الواقع ، يعد إعداد ItemTouchHelper أمرًا ممتعًا للغاية. من أجل عدم زيادة حجم هذه المقالة ، قسمتها إلى عدة.


كود المصدر


راجع الرمز الكامل لسلسلة المقالات هذه على مستودعات Android-ItemTouchHelper-Demo GitHub. تتناول هذه المقالة الالتزامات من ef8f149 إلى d164fba .


← اسحب وانتقد في RecyclerView. الجزء 1: ItemTouchHelper

Source: https://habr.com/ru/post/ar428419/


All Articles