最初の部分では、 ItemTouchHelperとItemTouchHelper.Callbackの実装を確認しました 。これは、基本的なドラッグアンドドロップおよびスワイプして 閉じる 機能をRecyclerView
追加します 。 この記事では、グリッド、ドラッグアンドドロップコントローラー、リストアイテムの選択、カスタムスワイプアニメーションの形式での要素の配置のサポートを追加することで、前の記事で行ったことを継続します。
ドラッグアンドドロップコントローラー
ドラッグ&ドロップをサポートするリストを作成する場合、通常、アイテムをタッチでドラッグアンドドロップする機能を実装します。 これは、「編集モード」でのリストの明快さと使いやすさに貢献しており、重要なガイドラインでも推奨されています。 この例にドラッグアンドドロップコントローラーを追加するのは非常に簡単です。
まず、要素( item_main.xml )のlayout
更新しlayout
。
<?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>
ドラッグコントローラーに使用される画像は、 マテリアルデザインアイコンにあり、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); }
次に、 ItemViewHolder
ドラッグコントローラーのImageView
を定義します。
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
は、 OnStartDragListener
をフラグメントに追加する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
要素のルートFrameLayout
背景( background
プロパティ)を追加するか、 RecyclerListAdapter.ItemViewHolderコンストラクターで設定します。 次のようになります。
見た目はかっこいいですが、さらに制御したい場合があります。 これを行う1つの方法は、 ViewHolder
要素の状態変更ViewHolder
処理できるようにすることViewHolder
。 このため、 ItemTouchHelper.Callback
はさらに2つのメソッドを提供します。
-
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
RecyclerListAdapter.ItemViewHolder
実装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
とアダプターが密接に接続されている場合、この設定を簡単に破棄し、 ItemTouchHelper.Callback
view
コンポーネントの状態を直接切り替えることができます。
ネット
GridLayoutManager
を使用しようとすると、正しく動作しないことがわかります。 理由と解決策は簡単です。アイテムの左右へのドラッグをサポートすることをItemTouchHelper
に伝える必要があります。 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
コンポーネントをレンダリングするプロセスに介入できます。 次のパートでは、この質問をさらに詳しく調べますが、ここでは、デフォルトのスワイプアニメーションをオーバーライドして線形消失を示す簡単な例を見てみましょう。
@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
およびdY
は、選択したview
コンポーネントに対する現在のシフトです。ここで、
- -1.0fは、右から左への
ItemTouchHelper.START
(ItemTouchHelper.END
からItemTouchHelper.END
へ) - 1.0fは左から右へ完全にスワイプします(
ItemTouchHelper.START
からItemTouchHelper.START
へ)
デフォルトのアニメーションをトリガーするには、処理していないactionState
に対してsuper
を呼び出すことが重要です。
次のパートでは、ドラッグアンドドロップ時に要素の描画を制御する例を検討します。
おわりに
実際、ItemTouchHelperのセットアップはとても楽しいです。 この記事の量を増やしないために、私はそれをいくつかに分けました。
ソースコード
Android-ItemTouchHelper-Demo GitHubリポジトリでこの記事シリーズの完全なコードを参照してください。 この記事では、 ef8f149からd164fbaへのコミットについて説明します 。
←RecyclerViewでドラッグしてスワイプします。 パート1:ItemTouchHelper