본문 바로가기
안드로이드 자바

[JAVA][Android] 리사이클러뷰 드래그엔 드롭 위치 수정

by teamnova 2023. 11. 6.

안녕하세요 오늘은 ItemTouchHelper 를 활용하여 리사이클러뷰 아이템 위치를 수정하는 예제를 만들어 보겠습니다.

 

ItemTouchHelper 란?

사용자의 터치 제스처를 통해 리사이클러뷰(RecyclerView) 아이템을 드래그하거나 스와이프하는 동작을 쉽게 구현할 있도록 도와주는 도구입니다. 이를 통해 개발자들은 사용자 친화적인 인터페이스를 구현하고, 아이템을 재배치하거나 삭제하는 등의 작업을 있습니다.

 

자세한 내용은 아래의 공식 문서를 확인해주세요

https://developer.android.com/reference/androidx/recyclerview/widget/ItemTouchHelper

 

ItemTouchHelper  |  Android Developers

androidx.core.os

developer.android.com

 

MainActivity.Java

public class MainActivity extends AppCompatActivity {

    // RecyclerView 객체를 선언합니다.
    RecyclerView recycler;

    // ItemTouchHelper 객체를 선언합니다.
    private ItemTouchHelper mItemTouchHelper;

    // Adepter 객체를 선언합니다.
    Adepter adapter;

    // 데이터를 담을 ArrayList를 초기화하고 샘플 데이터를 추가합니다.
    ArrayList<String> dataList = new ArrayList<>(Arrays.asList("가", "나", "다", "라", "마"));

    // 액티비티 생성 시 호출되는 메서드
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 레이아웃에서 정의한 RecyclerView를 연결합니다.
        recycler = findViewById(R.id.recycler);

        // Adepter 객체를 생성하고 데이터 리스트를 전달합니다.
        adapter = new Adepter(dataList);

        // RecyclerView에 레이아웃 매니저를 설정합니다.
        recycler.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

        // ItemTouchHelperCallback을 사용하여 ItemTouchHelper 객체를 생성합니다.
        mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback(adapter));

        // RecyclerView에 ItemTouchHelper를 연결합니다.
        mItemTouchHelper.attachToRecyclerView(recycler);

        // RecyclerView에 어댑터를 설정합니다.
        recycler.setAdapter(adapter);
    }
}

Adepter.Java

public class Adepter extends RecyclerView.Adapter<Adepter.ViewHolder> implements ItemTouchHelperListener {

    // 데이터 리스트를 저장할 ArrayList
    private ArrayList<String> dataList;

    // Adepter 생성자: 데이터 리스트를 받아옵니다.
    public Adepter(ArrayList<String> dataList) {
        this.dataList = dataList;
    }

    // ViewHolder 클래스: 각 아이템의 뷰 요소를 보유합니다.
    class ViewHolder extends RecyclerView.ViewHolder {
        TextView Content;

        // ViewHolder 생성자: 아이템의 뷰 요소를 초기화합니다.
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            Content = itemView.findViewById(R.id.Content);
        }
    }

    // onCreateViewHolder 메서드: 새로운 아이템 뷰를 생성하고 뷰 홀더를 반환합니다.
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        View view = layoutInflater.inflate(R.layout.item_view, parent, false);
        return new ViewHolder(view);
    }

    // onBindViewHolder 메서드: 뷰 홀더에 데이터를 바인딩하여 화면에 표시합니다.
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.Content.setText(dataList.get(position));
    }

    // getItemCount 메서드: 데이터 리스트의 크기를 반환합니다.
    @Override
    public int getItemCount() {
        return dataList.size();
    }

    // onItemMove 메서드: 아이템을 이동할 때 호출되며, 해당 위치의 데이터를 교체하고 화면 갱신을 알립니다.
    @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        String data = dataList.get(fromPosition);
        dataList.remove(fromPosition);
        dataList.add(toPosition, data);
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

    // onItemSwipe 메서드: 아이템을 스와이프할 때 호출되며, 여기서는 아무 작업도 수행하지 않습니다.
    @Override
    public void onItemSwipe(int position) {
    }
}

ItemTouchHelperCallback.java

// ItemTouchHelper.Callback 클래스를 상속받아서 사용자의 터치 동작을 처리하는 콜백 클래스입니다.
public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {

    // ItemTouchHelperListener 인터페이스를 구현한 객체를 저장하는 변수입니다.
    ItemTouchHelperListener listener;

    // 생성자를 통해 ItemTouchHelperListener 객체를 받아옵니다.
    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 | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        int swipe_flags = ItemTouchHelper.START | ItemTouchHelper.END;
        return makeMovementFlags(drag_flags, swipe_flags);
    }

    // 아이템이 이동할 때 호출되는 메서드입니다.
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView,
                          @NonNull RecyclerView.ViewHolder viewHolder,
                          @NonNull RecyclerView.ViewHolder target) {
        // ItemTouchHelperListener를 통해 아이템의 위치를 변경합니다.
        return listener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
    }

    // 아이템을 스와이프할 때 호출되는 메서드입니다.
    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        // ItemTouchHelperListener를 통해 아이템을 스와이프합니다.
        listener.onItemSwipe(viewHolder.getAdapterPosition());
    }

    // 길게 눌러서 드래그를 시작할 수 있는지 여부를 반환합니다.
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }
}

ItemTouchHelperListener.java(인터페이스)

public interface ItemTouchHelperListener {

    // 아이템을 이동할 때 호출되는 메서드
    boolean onItemMove(int fromPosition, int toPosition);

    // 아이템을 스와이프할 때 호출되는 메서드
    void onItemSwipe(int position);
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

item_view.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/Content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginBottom="16dp"
        android:text="TextView"
        android:textSize="26sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

결과 화면입니다.