Feature: Add drag-and-drop reordering for checklist items

Users can now reorder checklist items by dragging them up or down using the
drag handle icon (6-dot pattern) on each item.

Changes:
- Added ic_drag_handle.xml drawable with 6-dot pattern icon
- Updated item_checklist_row.xml to include drag handle ImageButton
- Modified ChecklistAdapter to support moveItem() operation
- Added ItemTouchHelper in NoteEditorFragment for drag-and-drop functionality
- Items reorder smoothly and changes are auto-saved
This commit is contained in:
Marvin 2026-03-17 19:41:37 -03:00
parent 30bfe441f8
commit 342262e892
4 changed files with 94 additions and 1 deletions

View File

@ -23,6 +23,7 @@ public class ChecklistAdapter extends RecyclerView.Adapter<ChecklistAdapter.Chec
void onTextChange(ChecklistItem item, String text); void onTextChange(ChecklistItem item, String text);
void onCheckedChange(ChecklistItem item, boolean checked); void onCheckedChange(ChecklistItem item, boolean checked);
void onDeleteClick(int position, ChecklistItem item); void onDeleteClick(int position, ChecklistItem item);
void onMoveItems(int fromPosition, int toPosition);
} }
public ChecklistAdapter(OnItemChangeListener listener) { public ChecklistAdapter(OnItemChangeListener listener) {
@ -88,13 +89,34 @@ public class ChecklistAdapter extends RecyclerView.Adapter<ChecklistAdapter.Chec
return items; return items;
} }
// Move item from one position to another
public void moveItem(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
ChecklistItem temp = items.get(i);
items.set(i, items.get(i + 1));
items.set(i + 1, temp);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
ChecklistItem temp = items.get(i);
items.set(i, items.get(i - 1));
items.set(i - 1, temp);
}
}
notifyItemMoved(fromPosition, toPosition);
}
static class ChecklistViewHolder extends RecyclerView.ViewHolder { static class ChecklistViewHolder extends RecyclerView.ViewHolder {
ImageButton dragHandle;
CheckBox checkBox; CheckBox checkBox;
EditText editText; EditText editText;
ImageButton deleteButton; ImageButton deleteButton;
public ChecklistViewHolder(@NonNull View itemView) { public ChecklistViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
dragHandle = itemView.findViewById(R.id.drag_handle);
checkBox = itemView.findViewById(R.id.check_box); checkBox = itemView.findViewById(R.id.check_box);
editText = itemView.findViewById(R.id.item_text); editText = itemView.findViewById(R.id.item_text);
deleteButton = itemView.findViewById(R.id.delete_button); deleteButton = itemView.findViewById(R.id.delete_button);

View File

@ -14,6 +14,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -175,12 +176,39 @@ public class NoteEditorFragment extends Fragment {
updateProgress(); updateProgress();
saveNoteImmediately(); saveNoteImmediately();
} }
@Override
public void onMoveItems(int fromPosition, int toPosition) {
scheduleSave();
}
}); });
checklistAdapter.setItems(note.getItems()); checklistAdapter.setItems(note.getItems());
checklistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); checklistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
checklistRecyclerView.setAdapter(checklistAdapter); checklistRecyclerView.setAdapter(checklistAdapter);
// Add drag-and-drop support
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
@Override
public boolean onMove(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();
int toPosition = target.getAdapterPosition();
checklistAdapter.moveItem(fromPosition, toPosition);
scheduleSave();
return true;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {}
});
itemTouchHelper.attachToRecyclerView(checklistRecyclerView);
addImageButton.setOnClickListener(v -> { addImageButton.setOnClickListener(v -> {
ChecklistItem newItem = new ChecklistItem(""); ChecklistItem newItem = new ChecklistItem("");
note.getItems().add(newItem); note.getItems().add(newItem);

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<!-- Top row of dots -->
<path
android:pathData="M9,3C10.105,3 11,3.895 11,5C11,6.105 10.105,7 9,7C7.895,7 7,6.105 7,5C7,3.895 7.895,3 9,3Z"
android:fillColor="@android:color/darker_gray"/>
<path
android:pathData="M15,3C16.105,3 17,3.895 17,5C17,6.105 16.105,7 15,7C13.895,7 13,6.105 13,5C13,3.895 13.895,3 15,3Z"
android:fillColor="@android:color/darker_gray"/>
<!-- Middle row of dots -->
<path
android:pathData="M9,12C10.105,12 11,12.895 11,14C11,15.105 10.105,16 9,16C7.895,16 7,15.105 7,14C7,12.895 7.895,12 9,12Z"
android:fillColor="@android:color/darker_gray"/>
<path
android:pathData="M15,12C16.105,12 17,12.895 17,14C17,15.105 16.105,16 15,16C13.895,16 13,15.105 13,14C13,12.895 13.895,12 15,12Z"
android:fillColor="@android:color/darker_gray"/>
<!-- Bottom row of dots -->
<path
android:pathData="M9,21C10.105,21 11,21.895 11,23C11,24.105 10.105,25 9,25C7.895,25 7,24.105 7,23C7,21.895 7.895,21 9,21Z"
android:fillColor="@android:color/darker_gray"/>
<path
android:pathData="M15,21C16.105,21 17,21.895 17,23C17,24.105 16.105,25 15,25C13.895,25 13,24.105 13,23C13,21.895 13.895,21 15,21Z"
android:fillColor="@android:color/darker_gray"/>
</vector>

View File

@ -1,16 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/item_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingVertical="4dp"> android:paddingVertical="4dp">
<ImageButton
android:id="@+id/drag_handle"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_drag_handle"
app:tint="@color/text_secondary" />
<CheckBox <CheckBox
android:id="@+id/check_box" android:id="@+id/check_box"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" /> android:layout_gravity="center_vertical"
android:layout_marginStart="4dp" />
<EditText <EditText
android:id="@+id/item_text" android:id="@+id/item_text"