본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[android] RecyclerView 에서 Drag 와 Swipe 구현하기

by 돼지왕 왕돼지 2020. 7. 29.
반응형



https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf


-

View.OnDragListener, GestureDetectors, onInterceptTouchEvent 등을 활용하는 방법이 주로 사용되고 있다. 하지만 이들은 복잡하다.



-

ItemTouchHelper 라는 녀석이 있다. 이 녀석은 Android Support Lib 에서 제공해주는 녀석으로 이 녀석을 쓰면 Drag&Drop, Swipe to dismiss 등을 아주 쉽게 구현할 수 있다.


ItemTouchHelper 는 RecyclerView.ItemDecoration 의 subclass 이다.

ItemDecoration 을 상속했다는 것은 LayoutManager 와 Adapter 에 쉽게 merge 할 수 있다는 것을 의미한다.



-

ItemTouchHelper 를 사용하기 위해서는 ItemTouchHelper.Callback 을 구현해야 한다.

이 녀석은 move 와 swipe events 에 대한 callback 을 받는다. view 의 selection 상태라던지 default animation 구현 등도 가능하다.


ItemTouchHelper.SimpleCallback 이라는 기본구현된 Callback 도 제공된다.

(https://developer.android.com/reference/android/support/v7/widget/helper/ItemTouchHelper.SimpleCallback.html



-

ItemTouchHelper 에서 drag&drop 과 swipe-to-dismiss 를 구현하기 위해 implement 해야 하는 함수들은 아래와 같다.


// 아래 3개 함수는 기능의 enability 에 대한 설정

getMovementFlags(RecyclerView, ViewHolder)

isLongPressDragEnabled()

isItemViewSwipeEnabled()


// 아래 2개 함수는 data 를 refresh 하기 위한 함수

onMove(RecyclerView, ViewHolder, ViewHolder)

onSwiped(ViewHolder, int)



-

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

getMovementFlags() 는 어떤 direction 으로 drag 와 swipe 를 support 할 것인지를 결정하는 역할을 한다.

ItemTouchHelper.makeMovementFlag(int, int) 를 통해 drag 를 지원할 방향과 swipe 를 지원할 방향을 지정해준다.

위의 코드에서는 UP, DOWN 에 대한 drag 를 지원하고, LEFT(START), RIGHT(END) 에 대한 swipe 를 지원한다.



-

@Override
public boolean isLongPressDragEnabled() {
    return true;
}

long press 를 했을 때 drag 를 지원하려면 true 를 return 해야 한다.

long press 없이 직접 drag 를 시작시키려면 ItemTouchHelper.startDrag(RecyclerView.ViewHolder) 를 호출해주면 된다.




-

@Override
public boolean isItemViewSwipeEnabled() {
    return true;
}

swipe 를 지원하려면 이 함수에서 true 를 return 해주어야 한다.

ItemTouchHelper.startSwipe(RecyclerView.ViewHoler) 를 호출하면 swipe 를 직접 수행시킬 수도 있다.



-

@Override
public void onItemDismiss(int position) {
    mItems.remove(position);
    mAdapter.notifyItemRemoved(position);
}

onItemDismiss 는 swipe 를 통해 view 가 사라졌을 때 불리는 callback 이다.



-

@Override
public boolean onItemMove(int fromPosition, int toPosition) {
    if (fromPosition < toPosition) {
        for (int i = fromPosition; i < toPosition; i++) {
            Collections.swap(mItems, i, i + 1);
        }
    } else {
        for (int i = fromPosition; i > toPosition; i--) {
            Collections.swap(mItems, i, i - 1);
        }
    }
    notifyItemMoved(fromPosition, toPosition);
    return true;
}

onItemMove 는 drag 를 통해 item 의 위치가 변경되었을 때 불린다.



-

준비가 다 되었다면 이제 준비한 ItemTouchHelper with Callback 을 recyclerView 에 붙여주어야 한다.

ItemTouchHelper.Callback callback = new MyItemTouchHelperCallback(adapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(recyclerView);


-

Drag 할 때 View 가 Select 되었다는 것을 보여주기 위해서는 ItemTouchHelper.Callback 의 다음 함수들을 구현해주면 된다.


onSelectedChanged(ViewHolder, int)

    drag 시에는 ACTION_STATE_DRAG 가, swipe 일때는 ACTION_STATE_SWIPE 가 전달된다. 

    참고로 drag 가 끝나거나 swipe 가 끝났을 때는 ACTION_STATE_IDLE 이 전달된다.


clearView(RecyclerView, ViewHolder)

    drag 된 view 가 drop 되었거나 swipe 가 cancel 되거나 complete 되었을 때 불린다.



-

RecyclerView.ItemDecoration ( ItemTouchHelper 의 super class ) 의 onChildDraw() 를 override 하여 child view drawing 에 관여할 수 있다.

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


-

끝! 아주 Easy.





반응형

댓글