0

I am trying to implement a swipe feature for recyclerview. I found this tutorial for that:

Swipe ⇆ Drag ⇅ Bind RecyclerView

The code is available here:

Code - GitHub

First of all I don't understand why is there an interface SwipeHandler which is implemented by MainActivity and another one within the SwipeItemTouchHelperCallback class? SwipeHandler is never used!? Is that a mistake?

Why am I asking this question? Because for my own project I have some trouble to connect the swipe off event with the appropriate callback within my fragment. The onItemSwiped method within the fragment is never called. So I changed the code a bit (I don't have a SwipeHandler...just use the interface within SwipeItemTouchHelper):

public class FragmentFlightRecords extends Fragment implements SwipeToDeleteCallback.OnItemSwipeListener {
    @Override
    public void onItemSwiped(int position) {
        saveAndRemoveItem(position);
        showSnackbar("Swiped Left " + position);
    }
}

layout.xml:

<variable
            name="handler"
            type="de.flightlogger.view.adapter.SwipeToDeleteCallback.OnItemSwipeListener" />

<androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rv_flight_records"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:adapter="@{adapter}"
                android:visibility="@{viewmodel.flightRecords.size() != 0}"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toTopOf="@id/cl_total_flight_time"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                bind:bgColorSwipe="@{@color/primaryDarkColor}"
                bind:drawableSwipe="@{@drawable/ic_add_black_24dp}"
                bind:onItemSwipe="@{(position) -> handler.onItemSwiped(position)}"
                bind:swipeEnabled="@{true}"
                tools:listitem="@layout/rv_item_flight_records"/>

SwipeToDeleteCallback:

public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback {

private Drawable icon;
private ColorDrawable background;
private OnItemSwipeListener onItemSwipeListener;
private boolean swipeEnabled;

private SwipeToDeleteCallback(int dragDirs, int swipeDirs) {
    super(dragDirs, swipeDirs);
}

private SwipeToDeleteCallback(Builder builder) {
    this(builder.dragDirs, builder.swipeDirs);
    background = builder.bgColorSwipe;
    icon = builder.drawableSwipe;
    swipeEnabled = builder.swipeEnabled;
    onItemSwipeListener = builder.onItemSwipeListener;
}

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

@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    return false;
}

@Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    int position = viewHolder.getAdapterPosition();
    onItemSwipeListener.onItemSwiped(position);
}


@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
    super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

    View itemView = viewHolder.itemView;
    int backgroundCornerOffset = 20; //so background is behind the rounded corners of itemView

    int iconMargin = (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
    int iconTop = itemView.getTop() + (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
    int iconBottom = iconTop + icon.getIntrinsicHeight();

    if (dX > 0) { // Swiping to the right
        int iconLeft = itemView.getLeft() + iconMargin;
        int iconRight = itemView.getLeft() + icon.getIntrinsicWidth() + iconMargin;
        icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);

        background.setBounds(itemView.getLeft(), itemView.getTop(),
                itemView.getLeft() + ((int) dX) + backgroundCornerOffset, itemView.getBottom());
    } else if (dX < 0) { // Swiping to the left
        int iconLeft = itemView.getRight() - iconMargin - icon.getIntrinsicWidth();
        int iconRight = itemView.getRight() - iconMargin;
        icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);

        background.setBounds(itemView.getRight() + ((int) dX) - backgroundCornerOffset,
                itemView.getTop(), itemView.getRight(), itemView.getBottom());
    } else { // view is unSwiped
        background.setBounds(0, 0, 0, 0);
    }

    background.draw(c);
    icon.draw(c);
}

public interface OnItemSwipeListener {
    void onItemSwiped(int position);
}

public static final class Builder {
    private int dragDirs, swipeDirs;
    private Drawable drawableSwipe;
    private ColorDrawable bgColorSwipe;
    private OnItemSwipeListener onItemSwipeListener;
    private boolean swipeEnabled;

    public Builder(int dragDirs, int swipeDirs) {
        this.dragDirs = dragDirs;
        this.swipeDirs = swipeDirs;
    }

    public Builder drawableSwipe(Drawable val) {
        drawableSwipe = val;
        return this;
    }

    public Builder bgColorSwipe(ColorDrawable val) {
        bgColorSwipe = val;
        return this;
    }

    public Builder onItemSwipeListener(OnItemSwipeListener val) {
        onItemSwipeListener = val;
        return this;
    }

    public Builder setSwipeEnabled(boolean val) {
        swipeEnabled = val;
        return this;
    }

    public SwipeToDeleteCallback build() {
        return new SwipeToDeleteCallback(this);
    }
}
}

Binding Adapter:

@BindingAdapter(value = {"swipeEnabled", "drawableSwipe", "bgColorSwipe", "onItemSwipe"}, requireAll = false)
    public static void setItemSwipeToRecyclerView(RecyclerView recyclerView, boolean swipeEnabled, Drawable drawableSwipe, ColorDrawable bgColorSwipe,
                                                  SwipeToDeleteCallback.OnItemSwipeListener onItemSwipe) {

        ItemTouchHelper.Callback swipeCallback = new SwipeToDeleteCallback
                .Builder(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT)
                .bgColorSwipe(bgColorSwipe)
                .drawableSwipe(drawableSwipe)
                .setSwipeEnabled(swipeEnabled)
                .onItemSwipeListener(onItemSwipe)
                .build();

        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeCallback);
        itemTouchHelper.attachToRecyclerView(recyclerView);
    }

The swiping itself works but it doesn't have a functionallity. The item of the recyclerview is not deleted and debuging tells me that the callback onItemSwiped within the fragment is never called. So what am I missing to hook up the swipe off event to the onItemSwiped callback within the fragment?

Peter
  • 579
  • 2
  • 7
  • 18

1 Answers1

0

I simply forgot to set the "handler" variable of the XML. So adding binding.setHandler(this) to the fragment solved the problem.

But I still don't understand why they have used two interfaces in the tutorial. I am just using the one of SwipeToDeleteCallback and it works fine.

Peter
  • 579
  • 2
  • 7
  • 18