如何在 Android 应用中实现 RecyclerView 的拖放功能?

androidmobile developmentapps/applications

在 Android 应用中,我们经常会看到数据以列表的形式呈现,例如在任务跟踪应用中,我们可以看到不同类型的任务按特定顺序排列成列表。在这些应用中,我们可以通过简单地更改任务的优先级来调整它们的优先级。我们将优先级最高的任务添加到顶部。因此,我们可以简单地将该任务拖放到最顶部的位置。这就是 RecyclerView 中用于排列这些项目的拖放功能。

实现

我们将创建一个简单的应用,其中我们将以 RecyclerView 的形式显示编程语言列表。

我们将为该 RecyclerView 添加功能,以便用户可以根据自己的喜好从该 RecyclerView 中拖放项目。

步骤 1:在 Android Studio 中创建新项目

按照下图所示,导航至 Android Studio。在下图所示的界面中,点击"新建项目"以创建一个新的 Android Studio 项目。

点击"新建项目"后,您将看到下图所示的界面。

在此界面中,我们只需选择"空 Activity",然后点击"下一步"。

点击"下一步"后,您将看到以下屏幕。

在此屏幕中,我们只需指定项目名称。然后,包名将自动生成。

注意 - 请确保选择 Java 作为语言。

指定所有详细信息后,点击"完成"以创建一个新的 Android Studio 项目。

项目创建完成后,我们将看到两个已打开的文件,即 activity_main.xml 和 MainActivity.java 文件。

步骤 2:使用 activity_main.xml

导航到 activity_main.xml。如果此文件不可见,请打开它。

在左侧面板中导航至 app>res>layout>activity_main.xml 以打开此文件。打开此文件后,将以下代码添加到其中。代码中添加了注释,以便您了解详细信息。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
   <!-- creating a recycler view on below line -->
   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/idRVLanguages"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
</RelativeLayout>

说明:在上面的代码中,我们创建了一个根布局,它使用 RelativeLayout。在这个布局中,我们创建了一个 RecyclerView,用于显示编程语言列表。

步骤 3:为 RecyclerView 的数据创建 Modal 类

导航到 app>java>你的应用包名>右键单击>新建>Java 类,并将其命名为 LanguageRVModal,然后将以下代码添加到其中。代码中添加了注释,以便详细了解。

package com.example.java_test_application;
public class LanguageRVModal {
   // 下面这行创建了一个用于存储语言名称的变量。
   private String languageName;
   // 下面这行创建构造函数、getter 和 setter。
   public LanguageRVModal(String languageName) {
       this.languageName = languageName;
   }
   public String getLanguageName() {
       return languageName;
   }
   public void setLanguageName(String languageName) {
       this.languageName = languageName;
   }
}

说明:在上面的代码中,我们创建了一个用于存储语言名称的变量,并为该变量创建了构造函数、getter 和 setter 方法。

步骤 4:为 RecyclerView 的每个项目创建布局文件

导航到 app>res>layout>,右键单击 > 新建布局资源文件,并将其命名为 language_rv_item,然后将以下代码添加到该文件中。代码中添加了注释,以便您详细了解。

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:id="@+id/idCVCard"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="5dp"
   app:cardCornerRadius="5dp">
   <!-- creating a relative layout on below line-->
   <RelativeLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_margin="4dp">
       <!-- creating a text view on below line -->
       <TextView
           android:id="@+id/idTVLanguage"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:padding="10dp"
           android:text="Language Name"
           android:textColor="@color/black"
           android:textSize="16sp"
           android:textStyle="bold" />
   </RelativeLayout>
</androidx.cardview.widget.CardView>

说明:在上面的代码中,我们创建了一个 Card View 作为根布局。在这个 Card View 内部,我们创建了一个 Relative Layout,并在该 Relative Layout 内部创建了一个 Text View,用于在 RecyclerView 中显示编程语言。

步骤 5:创建适配器类,用于为 RecyclerView 中的每个项目设置数据

导航到 app>java>您的应用程序包名>右键单击>新建一个 Java 类,并将其命名为 LanguageRVAdapter,然后将以下代码添加到该类中。代码中添加了注释,以便您详细了解。

package com.example.java_test_application;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Collections;
public class LanguageRVAdapter extends RecyclerView.Adapter<LanguageRVAdapter.ViewHolder> implements RecyclerRowMoveCallback.ItemTouchHelperContract {
   // 在下面这行创建数组列表和上下文的变量。
   private Context context;
   private ArrayList<LanguageRVModal> languageRVModals;
   // 下面这行代码创建了 recyclerview 的构造函数。
   public LanguageRVAdapter(Context context, ArrayList<LanguageRVModal> languageRVModals) {
       this.context = context;
       this.languageRVModals = languageRVModals;
   }
   @NonNull
   @Override
   public LanguageRVAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
       // 创建并初始化视图以加载布局。
       View view = LayoutInflater.from(context).inflate(R.layout.language_rv_item, parent, false);
       return new ViewHolder(view);
   }
   @Override
   public void onBindViewHolder(@NonNull LanguageRVAdapter.ViewHolder holder, int position) {
       // 将数据设置到我们的 RecyclerView 项中。
       holder.languageTV.setText(languageRVModals.get(position).getLanguageName());
   }
   @Override
   public int getItemCount() {
       // 返回数组列表的大小。
       return languageRVModals.size();
   }
   @Override
   public void onRowMoved(int fromPosition, int toPosition) {
       // 在下面这行设置中,行已移动,并且 RecyclerView 中的项目已交换。
       if (fromPosition < toPosition) {
           for (int i = fromPosition; i < toPosition; i++) {
               Collections.swap(languageRVModals, i, i + 1);
           }
       } else {
           for (int i = fromPosition; i > toPosition; i--) {
               Collections.swap(languageRVModals, i, i - 1);
           }
       }
       // 下面这行通知项目已被移动。
       notifyItemMoved(fromPosition, toPosition);
   }
   @Override
   public void onRowSelected(ViewHolder viewHolder) {
       // 在下一行设置选中项的背景颜色
       viewHolder.cardView.setBackgroundColor(Color.GRAY);
   }
   @Override
   public void onRowClear(ViewHolder viewHolder) {
       // 在下面这行设置清除行的颜色。
       viewHolder.cardView.setBackgroundColor(Color.WHITE);
   }
   public class ViewHolder extends RecyclerView.ViewHolder {
       // 下面这行创建了文本视图和卡片视图的变量。
       private TextView languageTV;
       private CardView cardView;
       public ViewHolder(@NonNull View itemView) {
           super(itemView);
           // 下面这行代码初始化这些变量。
           languageTV = itemView.findViewById(R.id.idTVLanguage);
           cardView = itemView.findViewById(R.id.idCVCard);
       }
   }
}

说明 - 上述代码加载了我们创建的布局文件,该文件将显示在 RecyclerView 的每个项目中。同时,它还用于设置 RecyclerView 每个项目的数据。

步骤 6:创建一个新的 Java 类,用于在 RecyclerView 中移动项目

导航到 app>java>,右键单击>新建 Java 类,并将其命名为 RecyclerRowMoveCallback,然后将以下代码添加到其中。代码中添加了注释,以便您详细了解。

package com.example.java_test_application;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
public class RecyclerRowMoveCallback extends ItemTouchHelper.Callback {
   // 下面这行创建了用于项目触摸辅助合约的变量。
   private final ItemTouchHelperContract itemTouchHelperContract;
   // 下面这行代码创建了一个构造函数。
   public RecyclerRowMoveCallback(ItemTouchHelperContract adapter) {
       itemTouchHelperContract = adapter;
   }
   @Override
   public boolean isItemViewSwipeEnabled() {
       return false;
   }
   @Override
   public boolean isLongPressDragEnabled() {
       return true;
   }
   @Override
   public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
       // 在下面这行设置中,拖动标志可以上下移动。
       int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
       return makeMovementFlags(dragFlags, 0);
   }
   @Override
   public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
       // 行已移动,请在下方设置行。
       this.itemTouchHelperContract.onRowMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
       return true;
   }
   @Override
   public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
   }
   @Override
   public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,
                                 int actionState) {
       // 在下一行更改所选项目。
       if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
           if (viewHolder instanceof LanguageRVAdapter.ViewHolder) {
               LanguageRVAdapter.ViewHolder myViewHolder =
                       (LanguageRVAdapter.ViewHolder) viewHolder;
               this.itemTouchHelperContract.onRowSelected(myViewHolder);
           }
       }
       super.onSelectedChanged(viewHolder, actionState);
   }
   @Override
   public void clearView(RecyclerView recyclerView,
                         RecyclerView.ViewHolder viewHolder) {
       super.clearView(recyclerView, viewHolder);
       // 在下面这行清除视图。
       if (viewHolder instanceof LanguageRVAdapter.ViewHolder) {
           LanguageRVAdapter.ViewHolder myViewHolder =
                   (LanguageRVAdapter.ViewHolder) viewHolder;
           this.itemTouchHelperContract.onRowClear(myViewHolder);
       }
   }
   // 下面这行创建了一个接口,并在其中创建了方法。
   public interface ItemTouchHelperContract {
       void onRowMoved(int fromPosition, int toPosition);
       void onRowSelected(LanguageRVAdapter.ViewHolder myViewHolder);
       void onRowClear(LanguageRVAdapter.ViewHolder myViewHolder);
   }
}

说明:在上面的代码中,我们创建了不同的方法,这些方法将用于拖放 RecyclerView 中的项目。

步骤 7:使用 MainActivity.java 文件

导航到 MainActivity.java 文件。如果该文件不可见,请在左侧面板中导航到 app>res>layout>MainActivity.java 以打开该文件。打开该文件后,将以下代码添加到其中。代码中添加了注释,以便您详细了解其含义。

package com.example.java_test_application;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
   // 在下面这行创建用于 RecyclerView 和适配器的变量。
   private RecyclerView recyclerView;
   private LanguageRVAdapter languageRVAdapter;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.activity_main);
        
        // 初始化变量(如下行)。
        
        recyclerView = findViewById(R.id.idRVLanguages);
        
        // 创建并初始化数组列表
        
        ArrayList languageRVModals = new ArrayList<>();
        
        // 向数组列表添加数据(如下行)。
        
        languageRVModals.add(new LanguageRVModal("Java"));
        
        languageRVModals.add(new LanguageRVModal("C"));
        
        languageRVModals.add(new LanguageRVModal("C++"));
        
        languageRVModals.add(new LanguageRVModal("Python"));
        
        languageRVModals.add(new LanguageRVModal("Kotlin"));
        
        languageRVModals.add(new LanguageRVModal("Dart"));
        
        // 初始化 RecyclerView 的适配器。
        
        languageRVAdapter = new LanguageRVAdapter(this, languageRVModals);
        
        // 设置 RecyclerView 的布局管理器。
        
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        
        // 设置 RecyclerView 的触摸辅助函数。
        
        ItemTouchHelper.Callback callback = new RecyclerRowMoveCallback(languageRVAdapter);
        
        // 在下一行初始化触摸辅助函数。
        
        ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
        
        // 将触摸辅助函数附加到 RecyclerView。
        
        touchHelper.attachToRecyclerView(recyclerView);
        
        // 在下一行设置 RecyclerView 的适配器。
        
        recyclerView.setAdapter(languageRVAdapter);
   }
}

说明:在上面的代码中,我们首先创建了 RecyclerView 和适配器的变量。接下来我们将看到 onCreate 方法。这是每个 Android 应用程序的默认方法。当应用程序视图创建时,会调用此方法。在 onCreate 方法中,我们设置内容视图,即名为 activity_main.xml 的布局文件,以便从该文件中设置 UI。在 onCreate 方法中,我们初始化了 RecyclerView 和适配器的变量。我们创建了一个数组列表并向其中添加数据。同时,我们将适配器配置到 RecyclerView 中。

我们还为其添加了一个项目触摸辅助功能,以便我们可以在 RecyclerView 中拖放项目。

添加上述代码后,现在只需点击顶部栏中的绿色图标即可在移动设备上运行我们的应用程序。

注意- 请确保您已连接到您的真机或模拟器。

输出

结论

在本文中,我们探讨了如何在 Android 应用程序中为 RecyclerView 实现拖放功能。


相关文章