Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 :)

like image 620
Fanney Avatar asked Oct 12 '16 10:10

Fanney


2 Answers

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.

like image 152
nyconing Avatar answered Oct 26 '22 17:10

nyconing


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

like image 27
xela2244 Avatar answered Oct 26 '22 18:10

xela2244