Trang chủHướng dẫnAndroid RecyclerView: Thêm chức năng kéo thả Item
Android

Android RecyclerView: Thêm chức năng kéo thả Item

CyStack blog 5 phút để đọc
CyStack blog03/09/2025
Locker Avatar

Chris Pham

Technical Writer

Locker logo social
Reading Time: 5 minutes

Hướng dẫn này sẽ giới thiệu cách triển khai chức năng kéo và thả item trong Android RecyclerView.

kéo và thả item trong Android RecyclerView.

Thêm chức năng kéo thả trong Android RecyclerView

Chức năng kéo và thả có thể được tích hợp vào RecyclerView thông qua lớp tiện ích ItemTouchHelper. Dưới đây là các phương thức quan trọng trong giao diện ItemTouchHelper.Callback cần được hiện thực:

  • isLongPressDragEnabled – trả về true để bật thao tác nhấn giữ trên từng dòng trong RecyclerView nhằm kích hoạt chức năng kéo và thả.
  • isItemViewSwipeEnabled – cho phép bật hoặc tắt tính năng vuốt. Trong hướng dẫn này, chúng ta sẽ vô hiệu hóa tùy chọn này.
  • getMovementFlags – cho phép chỉ định các cờ (flags) xác định hướng kéo và vuốt. Vì tính năng vuốt đã bị tắt, chúng ta sẽ truyền 0 cho phần vuốt.
  • onMove – xử lý logic khi thực hiện thao tác kéo và thả giữa các phần tử.
  • onSwipe – xử lý thao tác vuốt. Tuy nhiên, trong hướng dẫn hiện tại, phương thức này sẽ để trống.
  • onSelectedChanged – được gọi dựa trên trạng thái hiện tại của RecyclerView và việc phần tử có đang được nhấn giữ hoặc vuốt hay không. Tại đây, ta có thể tuỳ biến giao diện cho dòng dữ liệu, chẳng hạn như thay đổi màu nền khi đang được kéo.
  • clearView – được gọi khi người dùng kết thúc tương tác với một dòng trong RecyclerView.

Sau khi nắm các khái niệm cơ bản, chúng ta sẽ bắt đầu xây dựng ứng dụng Android với tính năng kéo và thả trong RecyclerView.

Cấu trúc dự án

Chức Năng Kéo Thả Item Android RecyclerView

Mã nguồn

Mã nguồn của tệp activity_main.xml, trong đó chỉ chứa một RecyclerView, được trình bày dưới đây:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="<https://schemas.android.com/apk/res/android>"
    xmlns:app="<https://schemas.android.com/apk/res-auto>"
    xmlns:tools="<https://schemas.android.com/tools>"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager" />

</LinearLayout>

Mã nguồn của tệp MainActivity.java được trình bày như sau:

package com.journaldev.androidrecyclerviewdraganddrop;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    RecyclerView recyclerView;
    RecyclerViewAdapter mAdapter;
    ArrayList<String> stringArrayList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerView);

        populateRecyclerView();
    }

    private void populateRecyclerView() {
        stringArrayList.add("Item 1");
        stringArrayList.add("Item 2");
        stringArrayList.add("Item 3");
        stringArrayList.add("Item 4");
        stringArrayList.add("Item 5");
        stringArrayList.add("Item 6");
        stringArrayList.add("Item 7");
        stringArrayList.add("Item 8");
        stringArrayList.add("Item 9");
        stringArrayList.add("Item 10");

        mAdapter = new RecyclerViewAdapter(stringArrayList);

        ItemTouchHelper.Callback callback =
                new ItemMoveCallback(mAdapter);
        ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
        touchHelper.attachToRecyclerView(recyclerView);

        recyclerView.setAdapter(mAdapter);
    }
}

Trong đoạn mã trên, chúng ta đã khởi tạo RecyclerViewAdapter.java với một ArrayList chứa các chuỗi. Một đối tượng của lớp ItemMoveCallback.java được gắn vào RecyclerView để kích hoạt chức năng kéo và thả. Dưới đây là mã nguồn của lớp ItemMoveCallback.java:

package com.journaldev.androidrecyclerviewdraganddrop;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;

public class ItemMoveCallback extends ItemTouchHelper.Callback {

    private final ItemTouchHelperContract mAdapter;

    public ItemMoveCallback(ItemTouchHelperContract adapter) {
        mAdapter = adapter;
    }

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

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

    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        return makeMovementFlags(dragFlags, 0);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                          RecyclerView.ViewHolder target) {
        mAdapter.onRowMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (viewHolder instanceof RecyclerViewAdapter.MyViewHolder) {
                RecyclerViewAdapter.MyViewHolder myViewHolder =
                        (RecyclerViewAdapter.MyViewHolder) viewHolder;
                mAdapter.onRowSelected(myViewHolder);
            }
        }

        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);

        if (viewHolder instanceof RecyclerViewAdapter.MyViewHolder) {
            RecyclerViewAdapter.MyViewHolder myViewHolder =
                    (RecyclerViewAdapter.MyViewHolder) viewHolder;
            mAdapter.onRowClear(myViewHolder);
        }
    }

    public interface ItemTouchHelperContract {
        void onRowMoved(int fromPosition, int toPosition);
        void onRowSelected(RecyclerViewAdapter.MyViewHolder myViewHolder);
        void onRowClear(RecyclerViewAdapter.MyViewHolder myViewHolder);
    }
}

Tại đây, chúng ta đã định nghĩa một interface có tên ItemTouchHelperContract. Mỗi phương thức trong interface này sẽ được gọi từ các phương thức đã được triển khai trong interface ItemTouchHelper.Callback. Mã nguồn của lớp RecyclerViewAdapter.java được trình bày như sau:

package com.journaldev.androidrecyclerviewdraganddrop;

import android.graphics.Color;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Collections;

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> implements ItemMoveCallback.ItemTouchHelperContract {

    private ArrayList<String> data;

    public class MyViewHolder extends RecyclerView.ViewHolder {
        private TextView mTitle;
        View rowView;

        public MyViewHolder(View itemView) {
            super(itemView);
            rowView = itemView;
            mTitle = itemView.findViewById(R.id.txtTitle);
        }
    }

    public RecyclerViewAdapter(ArrayList<String> data) {
        this.data = data;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview_row, parent, false);
        return new MyViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.mTitle.setText(data.get(position));
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

    @Override
    public void onRowMoved(int fromPosition, int toPosition) {
        if (fromPosition < toPosition) {
            for (int i = fromPosition; i < toPosition; i++) {
                Collections.swap(data, i, i + 1);
            }
        } else {
            for (int i = fromPosition; i > toPosition; i--) {
                Collections.swap(data, i, i - 1);
            }
        }
        notifyItemMoved(fromPosition, toPosition);
    }

    @Override
    public void onRowSelected(MyViewHolder myViewHolder) {
        myViewHolder.rowView.setBackgroundColor(Color.GRAY);
    }

    @Override
    public void onRowClear(MyViewHolder myViewHolder) {
        myViewHolder.rowView.setBackgroundColor(Color.WHITE);
    }
}

Phương thức onRowMoved được định nghĩa trong giao diện Contract trước đó sẽ được gọi khi thao tác kéo thả hoàn tất. Tại đây, chúng ta thực hiện hoán đổi vị trí hai dòng trong ArrayList và gọi notifyItemMoved để cập nhật lại adapter.

Kết quả đầu ra của ứng dụng:

Chức Năng Kéo Thả Item Android RecyclerView

Hiện tại, chức năng kéo và thả được thực hiện bằng cách nhấn giữ ở bất kỳ vị trí nào trong mỗi dòng của RecyclerView. Tiếp theo, chúng ta sẽ tìm hiểu cách thực hiện thao tác tương tự nhưng chỉ khi nhấn vào một thành phần cụ thể trong dòng dữ liệu.

Kéo và thả sử dụng Handle

Để sử dụng một thành phần cụ thể (handle view) trong dòng RecyclerView làm điểm kích hoạt kéo và thả, chúng ta cần thực hiện các bước sau:

  • Đặt isLongPressDragEnabled thành false để vô hiệu hoá thao tác kéo và thả mặc định.
  • Tạo một giao diện như sau:
public interface StartDragListener {
    void requestDrag(RecyclerView.ViewHolder viewHolder);
}

  • Triển khai giao diện này trong MainActivity và truyền nó vào Adapter.
@Override
public void requestDrag(RecyclerView.ViewHolder viewHolder) {
    touchHelper.startDrag(viewHolder);
}

mAdapter = new RecyclerViewAdapter(stringArrayList, this);

Bên trong tệp RecyclerViewAdapter.java, thực hiện như sau:

holder.imageView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            mStartDragListener.requestDrag(holder);
        }
        return false;
    }
});

Bạn có thể tìm thấy mã nguồn được cập nhật trong liên kết tải xuống ở cuối hướng dẫn này. Kết quả đầu ra của ứng dụng sau khi cập nhật mã được trình bày dưới đây:

Chức Năng Kéo Thả Item Android RecyclerView

Đến đây, bài hướng dẫn của chúng ta cũng đã đến lúc phải khép lại. Toàn bộ mã nguồn của dự án được trình bày bên dưới:

AndroidRecyclerViewDragAndDrop

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất