Seret dan Geser di RecyclerView. Bagian 2: pengontrol seret dan lepas, kisi, dan animasi khusus

Seret dan Geser di RecyclerView. Bagian 2: pengontrol seret dan lepas, kisi, dan animasi khusus


Pada bagian pertama, kami melihat ItemTouchHelper dan implementasi ItemTouchHelper.Callback , yang menambahkan fungsi drag & drop dan swipe-to-dismiss ke RecyclerView . Di artikel ini, kami akan melanjutkan apa yang telah dilakukan di sebelumnya dengan menambahkan dukungan untuk mengatur elemen dalam bentuk kisi, drag and drop controller, pemilihan item daftar, dan animasi swipe kustom.


Jala elemen


Seret dan lepas pengontrol


Saat membuat daftar yang mendukung drag & drop , mereka biasanya menerapkan kemampuan untuk menarik dan menjatuhkan item dengan sentuhan. Ini berkontribusi pada kejelasan dan kemudahan penggunaan daftar dalam "mode pengeditan", dan juga direkomendasikan oleh pedoman materi. Menambahkan pengontrol seret dan lepas ke contoh kita sangat mudah.


Seret dan lepas pengontrol


Pertama, perbarui layout elemen ( 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> 

Gambar yang digunakan untuk pengontrol drag dapat ditemukan di ikon Desain Bahan dan ditambahkan ke proyek menggunakan plugin generator ikon yang nyaman di Android Studio.


Seperti yang disebutkan secara singkat di artikel sebelumnya, Anda dapat menggunakan ItemTouchHelper.startDrag(ViewHolder) untuk memulai tarik dan lepas secara terprogram. Jadi, yang perlu kita lakukan adalah memperbarui ViewHolder dengan menambahkan pengontrol seret dan mengatur pengendali sentuh sederhana yang akan memanggil startDrag() .


Kami membutuhkan antarmuka untuk mengirimkan peristiwa di sepanjang rantai:


 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); } 

Kemudian tentukan ImageView untuk pengontrol seret di ItemViewHolder :


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

dan perbarui 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 sekarang akan terlihat seperti ini .


Yang masih harus dilakukan adalah menambahkan OnStartDragListener ke fragmen:


 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 sekarang akan terlihat seperti ini . Sekarang, ketika Anda meluncurkan aplikasi, Anda dapat mulai menyeret dan menjatuhkan dengan menyentuh controller.


Menyeret Item Daftar


Menyoroti Item Daftar


Sekarang dalam contoh kita tidak ada indikasi visual dari item yang diseret. Jelas, ini seharusnya tidak, tetapi mudah diperbaiki. Menggunakan ItemTouchHelper Anda dapat menggunakan efek penyorotan item standar. Pada Lollipop dan versi Android yang lebih baru, lampu latar "menyebar" di atas elemen dalam proses berinteraksi dengannya; pada versi sebelumnya, elemen hanya mengubah warnanya menjadi gelap.


Untuk menerapkan ini dalam contoh kita, cukup tambahkan latar belakang (properti background ) ke root FrameLayout elemen FrameLayout atau atur dalam konstruktor RecyclerListAdapter.ItemViewHolder . Akan terlihat seperti ini:


Pemilihan Elemen


Ini terlihat keren, tetapi Anda mungkin ingin mengontrol lebih banyak lagi. Salah satu cara untuk melakukan ini adalah mengizinkan ViewHolder menangani perubahan status suatu elemen. Untuk ini, ItemTouchHelper.Callback menyediakan dua metode lagi:


  • onSelectedChanged(ViewHolder, int) dipanggil setiap kali keadaan elemen berubah untuk menyeret ( ACTION_STATE_DRAG ) atau menggesek ( ACTION_STATE_SWIPE ). Ini adalah tempat yang ideal untuk mengubah status komponen view menjadi aktif .
  • clearView(RecyclerView, ViewHolder) dipanggil ketika komponen view seret-dan-jatuhkan selesai, dan juga saat gesek selesai ( ACTION_STATE_IDLE ). Di sini, keadaan awal komponen view Anda biasanya dipulihkan.

Sekarang, mari kita selesaikan semuanya.


Pertama, buat antarmuka yang akan diimplementasikan oleh 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(); } 

Kemudian, di SimpleItemTouchHelperCallback terapkan metode yang sesuai:


 @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(); } } 

Sekarang yang tersisa adalah RecyclerListAdapter.ItemViewHolder mengimplementasikan ItemTouchHelperViewHolder :


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

Dalam contoh ini, kami cukup menambahkan latar belakang abu-abu saat elemen aktif, dan kemudian menghapusnya. Jika ItemTouchHelper dan adaptor Anda terhubung erat, Anda dapat dengan mudah meninggalkan pengaturan ini dan mengganti status komponen view secara langsung di ItemTouchHelper.Callback .


Jaring


Jika sekarang Anda mencoba menggunakan GridLayoutManager , Anda akan melihat bahwa itu tidak berfungsi dengan benar. Alasan dan solusinya sederhana: kita harus memberi tahu ItemTouchHelper kami bahwa kami ingin mendukung menyeret item ke kiri dan kanan. Sebelumnya di SimpleItemTouchHelperCallback kita sudah menentukan:


 @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); } 

Satu-satunya perubahan yang diperlukan untuk mendukung kisi adalah menambahkan bendera yang sesuai:


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

Namun, swipe-to-dismiss bukanlah perilaku yang sangat alami untuk elemen grid, jadi swipeFlags kemungkinan besar akan 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); } 

Untuk melihat contoh GridLayoutManager berfungsi, lihat RecyclerGridFragment . Begini tampilannya saat startup:


Elemen Grid


Animasi Geser Khusus


ItemTouchHelper.Callback menyediakan cara yang sangat nyaman untuk sepenuhnya mengendalikan animasi sambil menyeret atau menyapu. Karena ItemTouchHelper adalah sebuah RecyclerView.ItemDecoration , kita dapat melakukan intervensi dalam proses rendering komponen tampilan dengan cara yang sama. Pada bagian selanjutnya, kita akan memeriksa pertanyaan ini secara lebih rinci, tetapi untuk sekarang, mari kita lihat contoh sederhana dari menimpa animasi swipe default untuk menunjukkan hilangnya linear.


 @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); } } 

dX dan dY adalah pergeseran saat ini relatif terhadap komponen view dipilih, di mana:


  • -1.0f adalah gesekan penuh dari kanan ke kiri (dari ItemTouchHelper.END ke ItemTouchHelper.START )
  • 1.0f adalah gesekan penuh dari kiri ke kanan (dari ItemTouchHelper.START ke ItemTouchHelper.END )

Penting untuk memanggil super untuk tindakan apa pun yang tidak Anda proses untuk memicu animasi default.


Pada bagian selanjutnya, kita akan mempertimbangkan contoh di mana kita akan mengontrol gambar elemen pada saat drag and drop.


Kesimpulan


Sebenarnya, pengaturan ItemTouchHelper cukup menyenangkan. Agar tidak menambah volume artikel ini, saya membaginya menjadi beberapa.


Kode sumber


Lihat kode lengkap untuk seri artikel ini di repositori Android-ItemTouchHelper-Demo GitHub. Artikel ini mencakup komit dari ef8f149 hingga d164fba .


← Seret dan Gesek di RecyclerView. Bagian 1: ItemTouchHelper

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


All Articles