Fanney
Fanney

Reputation: 408

Why does my RecyclerView with ItemTouchHelper stop dragging after only one item, after overriding getItemViewType()?

I have a simple RecyclerView that uses ItemTouchHelper to both swipe and drag items. Everything worked fine until I needed to style the first and last items differently, so I needed to override the getItemViewType(int p) function in the adapter. After I did, the drag functionality stopped being fluent and always dropped the items after moving only one position up/down. This is my RecyclerView Adapter:

public class CurrentStopsAdapter extends RecyclerView.Adapter<CurrentStopsAdapter.ViewHolder> {
private RemoveFromTripListener removeFromTripListener = null;
private SwapPlacesTripListener swapPlacesTripListener = null;
private Context context;
private List<PlaceLink> stops;

public CurrentStopsAdapter(Context context, List<PlaceLink> stops,
                           RemoveFromTripListener removeListener, SwapPlacesTripListener swapListener) {
    this.context = context;
    this.stops = stops;
    removeFromTripListener = removeListener;
    swapPlacesTripListener = swapListener;
}

public interface RemoveFromTripListener {
    void onRemoveTripButtonClick(String id);
}

public interface SwapPlacesTripListener {
    void onSwapPlacesButtonClick(int first, int second);
}

@Override
public int getItemViewType(int position) {
    if (position == (getItemCount()-1)) {
        return -1;
    }
    return position;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case -1:
            return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.list_item_destination, parent, false));
        case 0:
            return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.list_item_origin, parent, false));
        default:
            return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.list_item_stop, parent, false));
    }
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bindStop(stops.get(position));
}

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

public void remove(int position) {
    if (removeFromTripListener != null) {
        removeFromTripListener.onRemoveTripButtonClick(stops.get(position).getId());
    }
    notifyItemRemoved(position);
}

public void swap(int firstPosition, int secondPosition) {
    if (swapPlacesTripListener != null) {
       swapPlacesTripListener.onSwapPlacesButtonClick(firstPosition, secondPosition);
    }
    notifyItemMoved(firstPosition, secondPosition);
}

public class ViewHolder extends RecyclerView.ViewHolder {
    public final TextView placeTitle;

    public ViewHolder(View view){
        super(view);
        placeTitle = (TextView) view.findViewById(R.id.stops_list_item_title);
    }

    public void bindStop(PlaceLink place){
        this.placeTitle.setText(place.getTitle());

    }
}
}

And My ItemTouchHelper:

public class TripTouchHelper extends ItemTouchHelper.SimpleCallback {
    private CurrentStopsAdapter currentStopsAdapter;

    public TripTouchHelper(CurrentStopsAdapter currentStopsAdapter){
        super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
        this.currentStopsAdapter = currentStopsAdapter;
    }

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

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        currentStopsAdapter.remove(viewHolder.getAdapterPosition());
    }

}

If I skip the Override on getItemViewType and use the same layout for all items, everything works fine but I would very much like to find a solutions to this problem. Any help would be really appreciated :)

Upvotes: 6

Views: 3033

Answers (3)

nyconing
nyconing

Reputation: 1136

Adapter thinks that the both ViewHolder you moved is considered different type because getItemViewType() returned the type unique. So it should re-render.

For example, you can return the type by following my procedure:

return 0 for r.layout.foo

return 1 for r.layout.bar

Then you can write own method/pair to check layout when you wanted the layout by type. If you still need the position, you can get it after ViewHolder is created:AdapterPosition.

If you needed position in OnCreateViewHolder(), you have something wrong and might defeated the purpose of ViewHolder. Considered doing that work when manipulating data or when onBindViewHolder() if not so expensive.

Upvotes: 3

xela2244
xela2244

Reputation: 27

Delete getItemViewType(int position) in your recyclerview adapter. I had the same problem, and this worked.

Upvotes: 1

parkske
parkske

Reputation: 1

I think you are supposed to return a "type" as int of the item in question, not the position. Meaning, for the header/footer types you could return one value (ex: 1), while for all other types you could return another value (ex: 2). Returning position sees every movement through your types as a different View type because position will be unique, which I assume halts the drag.

Upvotes: 0

Related Questions