profile image

L o a d i n g . . .

반응형

안드로이드 코틀린 스와이프 메뉴 구성

 Android Kotlin Swipe Menu

 

모바일 특성상 작은 화면에 많은 것을 보여주어야 하기 때문에 메뉴나 삭제와 같은 작은 동작들은 숨겨놓고는 한다.

그래서 대체로 자주 보이는 기능이 클릭을 하였을 때 펼쳐지는 메뉴 혹은 스와이프를 했을 때 나오는 메뉴 기능이다.

구현은 Recyclerview에다가 구현을 하였으며, 꼭 Recyclerview에다가 할 필요 없을 것이다. 라이브러리는 사용하지 않고 ItemTouchHelper를 사용하여 구현한 자료를 참고로 만들어 보았다. 사실 참고라기엔 중간에 망쳐서 다 가져다 썼다..

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:paddingBottom="10dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginStart="13dp"
        android:layout_marginEnd="13dp"
        android:background="@android:color/holo_red_dark">

        <TextView
            android:id="@+id/swipe_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end|center_vertical"
            android:layout_marginEnd="25dp"
            android:text="Delete"
            android:textColor="#ffffff" />

    </FrameLayout>

    <LinearLayout
        android:id="@+id/swipe_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginStart="13dp"
        android:layout_marginEnd="13dp"
        android:background="@android:color/darker_gray"
        android:orientation="vertical">

        <TextView
            android:id="@+id/recyclerview_item_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/holo_blue_dark"
            android:textSize="20dp"
            android:textStyle="bold"
            tools:text="title here" />

        <TextView
            android:id="@+id/recyclerview_item_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:text="content here" />

    </LinearLayout>

</FrameLayout>

일반 Recyclerview와 코드는 같고 item의 레이아웃만 뒤에 가려져 있는 스와이프 했을 때 나올 메뉴를 겹쳐서 만들어 놓아야 한다.

class SwipeHelperCallback : ItemTouchHelper.Callback() {
    private var currentPosition: Int? = null
    private var previousPosition: Int? = null
    private var currentDx = 0f
    private var clamp = 0f

    override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder
    ): Int {
        return makeMovementFlags(0, LEFT)
    }

    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
                        target: RecyclerView.ViewHolder): Boolean {
        return false
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
    }

    override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
        currentDx = 0f
        getDefaultUIUtil().clearView(getView(viewHolder))
        previousPosition = viewHolder.adapterPosition
    }

    override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
        viewHolder?.let {
            currentPosition = viewHolder.adapterPosition
            getDefaultUIUtil().onSelected(getView(it))
        }
    }

    override fun getSwipeEscapeVelocity(defaultValue: Float): Float {
        return defaultValue * 10
    }

    override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
        val isClamped = getTag(viewHolder)
        setTag(viewHolder, !isClamped && currentDx <= -clamp)
        return 2f
    }

    override fun onChildDraw(
        c: Canvas,
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        dX: Float,
        dY: Float,
        actionState: Int,
        isCurrentlyActive: Boolean
    ) {
        if (actionState == ACTION_STATE_SWIPE) {
            val view = getView(viewHolder)
            val isClamped = getTag(viewHolder)
            val x =  clampViewPositionHorizontal(view, dX, isClamped, isCurrentlyActive)
            currentDx = x
            getDefaultUIUtil().onDraw(
                c,
                recyclerView,
                view,
                x,
                dY,
                actionState,
                isCurrentlyActive
            )
        }
    }

    private fun clampViewPositionHorizontal(view: View, dX: Float, isClamped: Boolean,
                                            isCurrentlyActive: Boolean) : Float {
        val min: Float = -view.width.toFloat()
        val max = 0f
        val x = if (isClamped) {
            if (isCurrentlyActive) dX - clamp else -clamp
        } else {
            dX
        }
        return min(max(min, x), max)
    }

    // isClamped를 view의 tag로 관리
    private fun setTag(viewHolder: RecyclerView.ViewHolder, isClamped: Boolean) {
        viewHolder.itemView.tag = isClamped
    }

    private fun getTag(viewHolder: RecyclerView.ViewHolder) : Boolean {
        return viewHolder.itemView.tag as? Boolean ?: false
    }

    private fun getView(viewHolder: RecyclerView.ViewHolder) : View {
        return (viewHolder as SwipeAdapter.ViewHolder).itemView.swipe_view
    }

    fun setClamp(clamp: Float) {
        this.clamp = clamp
    }

    fun removePreviousClamp(recyclerView: RecyclerView) {
        if (currentPosition == previousPosition) {
            return
        }
        previousPosition?.let {
            val viewHolder
            	= recyclerView.findViewHolderForAdapterPosition(it) ?: return
            getView(viewHolder).translationX = 0f
            setTag(viewHolder, false)
            previousPosition = null
        }
    }
}

스와이프가 동작하는 controller이며 ItemTouchHelper를 상속받아서 만들어 졌다.

필수 메소드는 override를 당연하게 했다. 아직 나도 저 위에 코드를 제대로 이해를 못하였으므로.. 내가 보고 공부한 위 소스가 있는 사이트를 참고해놓겠다. 흙흙..

velog.io/@trycatch98/Android-RecyclerView-Swipe-Menu

 

Android - RecyclerView Swipe Menu

RecyclerView의 ItemTouchHelper를 이용하여 Swipe Menu 구현

velog.io

 

ps. swipe는 라이브러리 사용하세요...제발

반응형
복사했습니다!