woxblom / DragListView

Drag and drop to reorder items in a list, grid or board for Android. Based on RecyclerView. Also supports swiping items in a list.
Apache License 2.0
693 stars 177 forks source link

CRASH - Inconsistency detected. Invalid item position 0(offset:-1).state:2 com.woxthebox.draglistview.DragItemRecyclerView #196

Closed shukiTSP closed 2 years ago

shukiTSP commented 2 years ago

When trying to drag first item from column a to first position of column b app crashes ..

woxblom commented 2 years ago

Hm I can not reproduce this, is it everytime? Can you reproduce it with the sample app?

shukiTSP commented 2 years ago

https://www.loom.com/share/7097fdb557e24f0082995a615a4d7652

woxblom commented 2 years ago

Hm this video does not really give me more help to find the problem unfortunately. In the sample app I have provided in the lib it does not crash when doing this so I feel like the bug is in your code. Have you done some overrides or anything in your version of the adapter?

shukiTSP commented 2 years ago

` public class SprintDetailsv2Fragment extends BaseFragment {

FragmentSprintDetailsv2Binding binding;
private static final String TAG = "SprintDetailsv2Fragment";
Sprint activeSprint;
String projectId, sprintId;
LinearLayoutManager linearLayoutManager;
List<Sprint.Status> statusList = new ArrayList<>();

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    binding = DataBindingUtil.inflate(inflater, R.layout.fragment_sprint_detailsv2, container, false);
    projectId = SprintDetailsFragmentArgs.fromBundle(getArguments()).getProjectId();
    sprintId = SprintDetailsFragmentArgs.fromBundle(getArguments()).getSprintId();
    setupUI();
    getActiveSprintDetails();
    return binding.getRoot();
}

private void setupUI() {

}

private void getActiveSprintDetails() {
        }

private void setupBoard() {
    Log.d(TAG, "setupBoard: ");
    binding.sprintBoardView.setSnapToColumnsWhenScrolling(true);
    binding.sprintBoardView.setSnapToColumnWhenDragging(true);
    binding.sprintBoardView.setSnapDragItemToTouch(true);
    binding.sprintBoardView.setSnapToColumnInLandscape(false);
    binding.sprintBoardView.setColumnSnapPosition(BoardView.ColumnSnapPosition.CENTER);
    binding.sprintBoardView.setBoardListener(new BoardView.BoardListener() {
        @Override
        public void onItemDragStarted(int column, int row) {
            //Toast.makeText(getContext(), "Start - column: " + column + " row: " + row, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onItemDragEnded(int fromColumn, int fromRow, int toColumn, int toRow) {
            if (fromColumn != toColumn || fromRow != toRow) {
                //Toast.makeText(getContext(), "End - column: " + toColumn + " row: " + toRow, Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onItemChangedPosition(int oldColumn, int oldRow, int newColumn, int newRow) {
            //Toast.makeText(binding.sprintBoardView.getContext(), "Position changed - column: " + newColumn + " row: " + newRow, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onItemChangedColumn(int oldColumn, int newColumn) {

// TextView itemCount1 = binding.sprintBoardView.getHeaderView(oldColumn).findViewById(R.id.item_count); // itemCount1.setText(String.valueOf(binding.sprintBoardView.getAdapter(oldColumn).getItemCount())); // TextView itemCount2 = binding.sprintBoardView.getHeaderView(newColumn).findViewById(R.id.item_count); // itemCount2.setText(String.valueOf(binding.sprintBoardView.getAdapter(newColumn).getItemCount())); }

        @Override
        public void onFocusedColumnChanged(int oldColumn, int newColumn) {
            //Toast.makeText(getContext(), "Focused column changed from " + oldColumn + " to " + newColumn, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onColumnDragStarted(int position) {
            //Toast.makeText(getContext(), "Column drag started from " + position, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onColumnDragChangedPosition(int oldPosition, int newPosition) {
            //Toast.makeText(getContext(), "Column changed from " + oldPosition + " to " + newPosition, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onColumnDragEnded(int position) {
            //Toast.makeText(getContext(), "Column drag ended at " + position, Toast.LENGTH_SHORT).show();
        }
    });

    binding.sprintBoardView.setBoardCallback(new BoardView.BoardCallback() {
        @Override
        public boolean canDragItemAtPosition(int column, int dragPosition) {
            // Add logic here to prevent an item to be dragged
            return true;
        }

        @Override
        public boolean canDropItemAtPosition(int oldColumn, int oldRow, int newColumn, int newRow) {
            // Add logic here to prevent an item to be dropped
            return true;
        }
    });

    for (Sprint.Status status : statusList) {
        addColumn(status.getStatusName(), status.getTasks());
    }

}

private void addColumn(String name, List<Task> tasks) {
    Log.d(TAG, "addColumn: " + name);
    View header = View.inflate(context, R.layout.column_item, null);

    DraggableTaskAdapter draggableTaskAdapter = new DraggableTaskAdapter(tasks,
            R.id.rootCard,
            true);
    ((TextView) header.findViewById(R.id.sprintNameTv)).setText(name);

    ColumnProperties columnProperties = ColumnProperties.Builder
            .newBuilder(draggableTaskAdapter)
            .setLayoutManager(new LinearLayoutManager(context))
            .setHasFixedItemSize(true)
            .setColumnBackgroundColor(context.getResources().getColor(R.color.grey))
            .setItemsSectionBackgroundColor(Color.TRANSPARENT)
            .setHeader(header)

// .setFooter(footer) .setColumnDragView(header) .build();

    binding.sprintBoardView.addColumn(columnProperties);

}

package com.mydev.claritask.adapters;

import android.content.Context; import android.graphics.drawable.BitmapDrawable; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.HorizontalScrollView; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.PopupWindow; import android.widget.TextView;

import androidx.annotation.NonNull; import androidx.core.util.Pair; import androidx.recyclerview.widget.RecyclerView;

import com.google.android.material.card.MaterialCardView; import com.kaopiz.kprogresshud.KProgressHUD; import com.mydev.claritask.R; import com.mydev.claritask.models.responses.DefaultResponse; import com.mydev.claritask.models.responses.Task; import com.mydev.claritask.network.ClaritaskService; import com.mydev.claritask.utils.Callbacks; import com.mydev.claritask.utils.Utils; import com.woxthebox.draglistview.DragItemAdapter;

import java.util.ArrayList; import java.util.List;

import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response;

public class DraggableTaskAdapter extends DragItemAdapter<Task, DraggableTaskAdapter.ViewHolder> {

private int mLayoutId;
private int mGrabHandleId;
private boolean mDragOnLongPress;
KProgressHUD progressHUD;
Callbacks.PopupMenuListener popupMenuListener;
ClaritaskService service;

public void setPopupMenuListener(Callbacks.PopupMenuListener popupMenuListener) {
    this.popupMenuListener = popupMenuListener;
}

public DraggableTaskAdapter(List<Task> list, int grabHandleId, boolean dragOnLongPress) {
    mGrabHandleId = grabHandleId;
    mDragOnLongPress = dragOnLongPress;
    setItemList(list);
}

@Override
public long getUniqueItemId(int position) {
    return position;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.task_item, parent, false);
    return new ViewHolder(view);
}

class ViewHolder extends DragItemAdapter.ViewHolder {
    View view;
    private HorizontalScrollView tagScroll;
    private LinearLayout tagListLayout;
    private TextView seeMoreText;
    private TextView taskTitleText;
    private TextView descriptionText;
    private LinearLayout linearLayout;
    private TextView dueDateText;
    private RecyclerView teamList;
    private TextView projectText;
    private TextView dateAssignedText;
    private TextView morePeopleCountTv;
    MaterialCardView rootCard;
    ImageButton actionsImageBtn;
    Button completeBtn, moveBtn, deleteBtn;
    LinearLayout buttonsContainerLayout;
    LinearLayout peoplesLayout;
    private static final String TAG = "ViewHolder";

    public ViewHolder(View itemView) {
        super(itemView, mGrabHandleId, mDragOnLongPress);
        setupUI(itemView);
    }

    private void setupUI(View itemView) {
        view = itemView;
        rootCard = (MaterialCardView) itemView.findViewById(R.id.rootCard);
        tagScroll = (HorizontalScrollView) itemView.findViewById(R.id.tag_scroll);
        tagListLayout = (LinearLayout) itemView.findViewById(R.id.tag_list_layout);
        peoplesLayout = (LinearLayout) itemView.findViewById(R.id.peoplesLayout);
        seeMoreText = (TextView) itemView.findViewById(R.id.see_more_text);
        morePeopleCountTv = (TextView) itemView.findViewById(R.id.morePeopleCountTv);
        taskTitleText = (TextView) itemView.findViewById(R.id.task_title_text);
        descriptionText = (TextView) itemView.findViewById(R.id.description_text);

// linearLayout = (LinearLayout) itemView.findViewById(R.id.linearLayout); dueDateText = (TextView) itemView.findViewById(R.id.due_date_text); // teamList = (RecyclerView) itemView.findViewById(R.id.team_list); projectText = (TextView) itemView.findViewById(R.id.project_text); actionsImageBtn = (ImageButton) itemView.findViewById(R.id.actionsImgBtn); dateAssignedText = (TextView) itemView.findViewById(R.id.date_assigned_text);

        actionsImageBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showTaskPopupMenu(view.getContext());
            }
        });

        progressHUD = Utils.getProgressBar(view.getContext());

    }

    private void showTaskPopupMenu(Context context) {
        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View popupView = layoutInflater.inflate(R.layout.task_popup_menu, null);
        initPopupView(popupView);
        PopupWindow popupWindow = new PopupWindow(
                popupView,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        );
        popupWindow.setBackgroundDrawable(new BitmapDrawable());
        popupWindow.setOutsideTouchable(true);
        popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {

            }
        });
        popupWindow.showAsDropDown(actionsImageBtn);

        for (int i = 0; i < buttonsContainerLayout.getChildCount(); i++) {
            View v = buttonsContainerLayout.getChildAt(i);
            if (v instanceof Button) {
                Log.d(TAG, "showPopupMenu: ");
                v.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Log.d(TAG, "onClick: " + view.getId());
                        popupWindow.dismiss();
                        if (popupMenuListener != null) {
                            popupMenuListener.onPopupItemClicked(view.getId(), getAdapterPosition());
                        }
                        if (view.getId() == R.id.deleteBtn) {
                            showDeleteDialog(v.getContext(), mItemList.get(getAdapterPosition()));
                        } else if (view.getId() == R.id.completeBtn) {
                            completeTask(mItemList.get(getAdapterPosition()));
                        } else {

                        }
                    }
                });
            }
        }
    }

    private void initPopupView(View popupView) {
        moveBtn = (Button) popupView.findViewById(R.id.moveTaskBtn);
        completeBtn = (Button) popupView.findViewById(R.id.completeBtn);
        deleteBtn = (Button) popupView.findViewById(R.id.deleteBtn);
        buttonsContainerLayout = (LinearLayout) popupView.findViewById(R.id.buttonsContainerLayout);

// if (isSprint) { moveBtn.setVisibility(View.VISIBLE); // } }

}

} `

woxblom commented 2 years ago

Yeah sorry it is very hard for me to just debug the problem by looking at the code like that. Try to put a break point just before the crash and see the status of the different adapter lists, for some reason the number in the lists is wrong when the item is switching column.

woxblom commented 2 years ago

Can not reproduce and no answer in 3 weeks, closing.