【Android Studio】RecyclerViewスワイプ削除時の背景アレンジ方法(onChildDraw)

engineer



RecyclerViewのスワイプ削除時、「現在削除している」とはっきり分かるように背景をアレンジする方法についてメモしています(実際の挙動は以下の動画を参照)。

スワイプして削除する方法を含む基本的なRecyclerViewについてはこちらの記事をご覧ください。




FrameLayoutでレイアウトをあらかじめ重ねておく方法


簡単で、アレンジしやすい方法は、あらかじめレイアウトを重ねておき、スワイプした際に前面のみ移動させ、背面のレイアウトを表示させる方法です。


1行分のレイアウトXML(適当です)は以下の通りです。
FrameLayoutにて
-削除用背景(赤 + ゴミ箱アイコン:背面)
-データ行(緑:前面)
を重ねています。
FrameLayoutでは後に来るものが前面に重ねられていくようなので、このような順序にしています。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/frameLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#2196F3">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/viewbackground"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="@android:color/holo_red_dark"
        android:padding="20dp">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            android:layout_weight="1"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:srcCompat="@android:drawable/ic_menu_delete" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/viewforeground"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="#AEEDAA"
        android:padding="20dp">


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <TextView
                android:id="@+id/titleView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="TextView" />

            <View
                android:layout_width="0dp"
                android:layout_height="1dp"
                android:layout_weight="1" />

            <TextView
                android:id="@+id/dateView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="TextView" />
        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

</FrameLayout>



スワイプ時の挙動を制御するコード部分です。
前述した過去記事のMainActivity内ItemTouchHelperに追記しています。そちらも参考にしてください。

itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {
            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder
            ): Boolean {
                //動かす処理(省略)
            }

            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                viewHolder.let{
                    realm.executeTransaction{
                        //Realmデータ削除処理(省略)
                    }
                    recyclerView.adapter?.notifyDataSetChanged()
                }
            }

       //選択して移動させる度に呼ばれる
            override fun onChildDraw(
                c: Canvas,
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                dX: Float,
                dY: Float,
                actionState: Int,
                isCurrentlyActive: Boolean
            ) {
                super.onChildDraw(
                    c,
                    recyclerView,
                    viewHolder,
                    dX,
                    dY,
                    actionState,
                    isCurrentlyActive
                )
                val holder = viewHolder as ViewHolder

                if (dX < 0) {
                    val holder = viewHolder as ViewHolder
                    //frameLayoutを元の位置に固定
                    getDefaultUIUtil().onDraw(
                        c,
                        recyclerView,
                        holder.frameLayout,
                        0f,
                        0f,
                        actionState,
                        isCurrentlyActive
                    )
            //foreground(前面)のみを指の位置に追従させる
                    getDefaultUIUtil().onDraw(
                        c,
                        recyclerView,
                        holder.foreground,
                        dX,
                        dY,
                        actionState,
                        isCurrentlyActive
                    )
                }
            }

       //選択されたら呼ばれる
            override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
                super.onSelectedChanged(viewHolder, actionState)
                if(viewHolder != null) {
                    val holder = viewHolder as ViewHolder
                    getDefaultUIUtil().onSelected(holder.foreground)
                }
            }

       //移動し終えたら呼ばれる
            override fun clearView(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder
            ) {
                super.clearView(recyclerView, viewHolder)
                val holder = viewHolder as ViewHolder
                getDefaultUIUtil().clearView(holder.foreground)
                getDefaultUIUtil().clearView(holder.background)
            }
        })
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }


① onChildDraw

移動させる度に呼ばれます。
getDefaultUIUtil()でItemTouchUIUtilインスタンスを受け取り、onDrawメソッドでスワイプ時の移動を制御しています。

onChildDrawの引数について:
dX, dYについては、以下の通りです。左にスワイプ する際はdX < 0となります。


②onSelectedChanged
選択された際に呼ばれます。上の例ではやっていませんが、例えば選択された行の色
変更する場合はこちらで行えます。以下の例を見てください。

//選択されたら呼ばれる
            override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
                super.onSelectedChanged(viewHolder, actionState)
                if(viewHolder != null) {
                    val holder = viewHolder as ViewHolder
                    holder.itemView.viewforeground.setBackgroundColor(Color.BLUE)
                }
            }


③clearView
移動し終えたら呼ばれるメソッドです。getDefaultUIUtil().clearViewで選択状態における様々な変更をリセットしています。
例えば②で選択時に色を変更した場合、clearViewを忘れると指を離した場合もその色のままになってしまいます。





他にもRectFなどで図形を描画する方法などもあるようです。
別途まとめたいと思います。


コメント

  1. スミス より:

    android勉強中の初心者です。

    recyclerViewのスワイプメニューの実装方法を調べていたらここに辿り着きました。getDefaultUIUtil()を使えばなんとなく糸口が掴めそうな気がします。この手の情報は日本語で説明されたサイトが少ないのでとても助かります。

    できれば!recyclerViewのスワイプメニューの具体的な実装方法について説明付きで紹介してもらえると助かります。。

タイトルとURLをコピーしました