Hun's Blog

Android RecyclerView + ItemTouchHelper - 드래그 앤 드롭, 스와이프 본문

Android

Android RecyclerView + ItemTouchHelper - 드래그 앤 드롭, 스와이프

jhk-im 2020. 3. 22. 16:36

RecyclerView + ItemTouchHelper -> Drag and Drop과 swipe 구현 

 

실행화면

 

자바 클래스 및 인터페이스

 

레이아웃

activity_main 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
        android:id="@+id/rv_tab_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:listitem="@layout/tab_item"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
 
 

tools:listitem -> 리사이클러뷰에 추가 될 레이아웃을 지정한다. 

 

tab_item.xml -> 추가될 아이템 레이아웃 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:minHeight="60dp"
    android:orientation="horizontal"
    android:paddingEnd="20dp"
    android:paddingStart="20dp">
 
 
    <TextView
        android:id="@+id/name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:textColor="@android:color/primary_text_light"
        android:textSize="20sp"
        android:text="TAB NAME" />
 
 
    <FrameLayout
        android:id="@+id/drag_handle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <ImageView
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_gravity="center"
            android:src="@drawable/ic_drag_handle" />
    </FrameLayout>
 
</LinearLayout>
 
 

 

아이템 레이아웃

TabItem 모델

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class TabItem {
 
    private String name;
    private int number;
 
    public TabItem(String name, int number){
        this.name = name;
        this.number = number;
    }
 
    public int getNumber() {
        return number;
    }
 
    public void setNumber(int number) {
        this.number = number;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}
 
 

아이템 레이아웃에 셋팅 될 Tab name과 리스트 순번을 모델 객체가 저장하고 반환한다. 

 

 

ItemTouchHelperListener 인터페이스

1
2
3
4
public interface ItemTouchHelperListener {
    boolean onItemMove(int form_position, int to_position);
    void onItemSwipe(int position);
}
 
 

RecylcerView의 Adapter 클래스에서 상속받는다. 

onItemMove -> 아이템의 현재 위치와 이동 위치를 입력받아 아이템 리스트에의 위치를 수정하도록 구현 

onItemSwipe -> 아이템의 포지션값을 받아 해당 아이템을 Swipe할때 로직을 구현 

 

ItemTouchHelperCallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
 
    private ItemTouchHelperListener listener;
 
    public ItemTouchHelperCallback(ItemTouchHelperListener listener) { this.listener = listener; }
 
    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView,
                                @NonNull RecyclerView.ViewHolder viewHolder) {
        int drag_flags = ItemTouchHelper.UP|ItemTouchHelper.DOWN;
        int swipe_flasg = ItemTouchHelper.START|ItemTouchHelper.END;
        return makeMovementFlags(drag_flags,swipe_flasg);
    }
 
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView,
                          @NonNull RecyclerView.ViewHolder viewHolder,
                          @NonNull RecyclerView.ViewHolder target) {
        return listener.onItemMove(viewHolder.getAdapterPosition(),target.getAdapterPosition());
    }
 
    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        listener.onItemSwipe(viewHolder.getAdapterPosition());
    }
 
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
 

 

ItemTouchHelperCallback 생성자

-> 해당 클래스를 생성할 때 ItemTouchHelperListener를 입력받아 멤버 인스턴스로 지정하고 ItemTouchHelper.Callback이 지원하는 여러 메소드를 오버라이딩 하여 필요에 따라 사용한 후  결과값들을 반환해 준다.  아이템 위치변경 , 스와이프 , 롱클릭 등 다양한 기능을 지원한다. 

 

getMovementFlags

-> 리사이클러뷰와 리사이클러뷰 뷰홀더를 입력받는다.

-> drag 위치와 swipe 위치를 ItemTouchHelper에서 받아 셋팅한다.

-> makeMovementFlags() 메소드로 drag위치와 swipe 위치를 입력하여 현재 위치값을 int로 반환해준다. 

 

onMove

-> 리사이클러뷰,viewHolder, target(viewHolder 중 선택된 아이템)을 입력받아 움직임을 감지한다. 

-> ItemTouchHelperListener의 onItemMove메소드로 해당 아이템의 움직임을 감지한다.

-> onItemMove 메소드는 아이템이 움직이고 있는 가를 판별하고 boolean 값으로 반환한다. 

 

onSwiped

-> 리사이클러뷰의 뷰홀더와 움직일 방향을 입력받는다.

-> ItemTouchHelperListner의 onItemSwipe메소드에 움직일 방향을 입력하여 swipe를 구현한다. 

 

isLongPressDragEnabled() 

-> true를 반환하도록 설정하면 롱클릭을 감지한다. 

 

TabListAdpater

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class TabListAdapter extends RecyclerView.Adapter<TabListAdapter.ItemViewHolder>
                            implements ItemTouchHelperListener{
 
    private ArrayList<TabItem> items = new ArrayList<>();
 
    public TabListAdapter(){}
 
    @NonNull
    @Override
    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.tab_item, parent, false);
        return new ItemViewHolder(view);
    }
 
    @Override
    public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {
    }
 
    @Override
    public int getItemCount() {
        return items.size();
    }
 
    public void setItems(ArrayList<TabItem> itemList){
        items = itemList;
        notifyDataSetChanged();
    }
 
    @Override
    public boolean onItemMove(int form_position, int to_position) {
        TabItem item = items.get(form_position);
        items.add(to_position,item);
        item.setNumber(to_position);
        notifyItemMoved(form_position, to_position);
        return true;
    }
 
    @Override
    public void onItemSwipe(int position) {
        items.remove(position);
        notifyItemRemoved(position);
    }
 
    public class ItemViewHolder extends RecyclerView.ViewHolder {
        TextView tabName;
        public ItemViewHolder(@NonNull View itemView) {
            super(itemView);
            tabName = itemView.findViewById(R.id.name);
        }
        public void onBind(TabItem item,int position){
            tabName.setText(item.getName());
            item.setNumber(position);
        }
    }
}
 
 

-> RecyclerView.Adapter 를 상속받는다.

Adapter에는 RecyclerView.ViewHolder를 상속받은 내부 클래스를 구현하고 제네릭으로 지정하여 상속받아야 한다. 

 

-> ItemTouchHelperListener를 상속받아 onItemMove과 onItemSwipe를 오버라이딩하여 행동을 구현해준다. 

 

-> TabItem 모델을 제네릭으로 지정한 ArrayList를 생성한다. 

 

--

RecyclerView 상속 오버라이딩 메소드 

 

onCreateViewHolder

-> 리사이클러뷰에 추가될 item 레이아웃을 연결한다. 

-> ItemViewHolder 클래스에 View(item레이아웃)를 입력한다. 

 

onBindViewHolder 

-> ItemViewHolder와 아이템의 리스트상 위치를 입력받는다. 

-> ItemViewHolder 내부에 구현한 onBind 메소드에 아이템 리스트에서 포지션값으로 아이템을 선택하여 입력한다. 

 

getItemCount

-> items의 전체 갯수 숫자를 반환한다. 

 

--

 

setItems

-> 메인액티비티에서 TabItem 객체를 생성한 후에 리스트를 입력하여 어댑터의 멤버 items에 매치시킨다. 

-> notifyDataSetChanged메소드로 데이터가 셋팅되었음을 알린다. 

--

 

ItemTouchHelperListener 오버라이딩 메소드

 

onItemMove

-> 아이템의 리스트상 현재위치와 움직일 위치를 입력받느다.

-> 입력받은 값으로 아이템의 이동을 구현한다. 

-> notifyItemMoved 메소드로 데이터가 이동함을 알린다. 

 

onItemSwipe

-> 포지션값을 입력받는다.

-> 포지션값으로 아이템리스트의 해당 포지션 아이템을 삭제한다.

-> notifyItemRemoved 메소드로 데이터가 삭제되었음을 알린다. 

 

--

ItemViewHolder 클래스

 

ItemViewHolder

-> 아이템 레이아웃의 name TextView 와 연결한다. 

 

onBind

-> TabItem객체에 셋팅된 값으로 각각의 레이아웃에 셋팅한다. 

 

 

MainActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class MainActivity extends AppCompatActivity {
 
    private RecyclerView mRecyclerView;
    private TabListAdapter mAdapter;
    private ItemTouchHelper mItemTouchHelper;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        mRecyclerView = (RecyclerView) findViewById(R.id.rv_tab_list);
 
        LinearLayoutManager manager = new LinearLayoutManager(getApplicationContext());
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(manager);
 
        mAdapter = new TabListAdapter();
        mRecyclerView.setAdapter(mAdapter);
 
        mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback(mAdapter));
 
        mItemTouchHelper.attachToRecyclerView(mRecyclerView);
 
        ArrayList<TabItem> items = new ArrayList<>();
        TabItem item1 = new TabItem("SUBSCRIBE",0);
        TabItem item2 = new TabItem("BEST",0);
        TabItem item3 = new TabItem("MUSIC",0);
        TabItem item4 = new TabItem("SPORTS",0);
        TabItem item5 = new TabItem("GAME",0);
        TabItem item6 = new TabItem("MOVIE",0);
        TabItem item7 = new TabItem("NEWS",0);
        TabItem item8 = new TabItem("LIVE",0);
        items.add(item1);
        items.add(item2);
        items.add(item3);
        items.add(item4);
        items.add(item5);
        items.add(item6);
        items.add(item7);
        items.add(item8);
 
        mAdapter.setItems(items);
 
    }
 
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

-> 리사이클러뷰를 생성한다.

 

-> 리사이클러뷰에 LinearLayoutManager를 활용해 속성을 지정한다. 

 

-> 리사이클러뷰에 TabListAdpater를 셋팅한다. 

 

-> 어댑터를 입력한 ItemTouchHelperCallback 클래스를 ItemTouchHelper 생성자에 입력하여 생성한다.

 

-> TouchHelper 의 attachToRecyclerView 메소드를 활용해 Touch를 구현할 리사이클러뷰를 연결한다. 

 

-> TabItem 을 제네릭으로 받는 ArrayList를 생성하고 임의의 값을 셋팅한다.

 

-> 어댑터의 setItems 메소드에 해당 ArrayList를 입력한다. 

 

 

참고

https://fullstatck.tistory.com/15

 

[Android] RecyclerView 에서 드래그앤드롭(drag&drop)과 스와이프(swpie&dismiss) 사용하기

안드로이드 RecyclerView에서 drag&drop과 swipe-to-dismiss를 구현한 예제는 많지만 View.OnDragListener를 이용한 경우가 많다. 이것은 예전 버전을 사용한 경우이고 새로운 API를 사용하거나 GestureDetector나..

fullstatck.tistory.com

 

https://everyshare.tistory.com/27

 

[안드로이드] 리싸이클러뷰 아이템 이동, 삭제 - RecyclerView, ItemTouchHelper

ItemTouchHelper를 이용해서 리싸이클러뷰의 아이템 이동, 삭제 이벤트 예제를 만들어보겠습니다. -롱클릭 후 위아래로 움직이면 순서 변경 -아이템을 왼쪽이나 오른쪽으로 스와이프하면 삭제 리싸이클러뷰의 기..

everyshare.tistory.com

https://developer.android.com/guide/topics/ui/drag-drop?hl=ko

 

드래그 앤 드롭  |  Android Developers

Android 드래그 앤 드롭 프레임워크를 사용하면 사용자가 그래픽 드래그 앤 드롭 동작을 사용하여 한 보기에서 다른 보기로 데이터를 이동할 수 있습니다. 프레임워크로는 드래그 이벤트 클래스, 드래그 리스너, 도우미 메서드, 클래스 등이 있습니다. 프레임워크는 주로 데이터 이동에 맞게 디자인되었지만 다른 UI 작업에 사용할 수도 있습니다. 예를 들어 사용자가 색상 아이콘을 다른 아이콘 위로 드래그하면 색상을 혼합하는 앱을 만들 수 있습니다. 이 주제의 나

developer.android.com