简体   繁体   中英

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

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.

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM