Как расширить RecyclerView, чтобы разрешить «слияние» элементов? - PullRequest
1 голос
/ 16 января 2020

Я пытаюсь осуществить «объединение» элементов с помощью RecyclerView.

У нас есть список карточек, которые можно пролистывать, редактировать и изменять порядок. У нас также есть понятие «стопки», где вы можете объединить карты в стопку. Ранее вы могли загружать только предустановленные стеки содержимого, но сейчас я пытаюсь реализовать пользовательские стеки. Ниже приведен скриншот карт «в состоянии покоя»:

Screenshot of cards

Реорганизовать процесс переупорядочения было действительно легко - я просто подключился к ItemTouchListener.Callback, чтобы разобраться с выбором вверх и падение карт. Тем не менее, мне действительно трудно слиться с работой. Функциональность заключается в том, что когда вы отпускаете перетаскиваемую карту поверх другой карты, она должна вызывать некоторый код в другом месте, чтобы объединить эти стеки / карты в новый стек. *

  1. Используйте chooseDropTarget, чтобы сохранить ссылку на последнюю карту, над которой мы закончили
  2. Используйте onDraw, чтобы уничтожить эту ссылку, если мы больше не за этой картой
  3. Используйте clearView, чтобы объединить два стека, если они все еще лежат друг над другом, когда мы выпускаем карту.

Это работает, но объединение очень противоречиво. Я думаю, проблема в том, что clearView вызывается после всех анимаций, которые, я думаю, включают в себя анимацию возврата в положение / перемещение. Поэтому в зависимости от того, как взаимодействуют потоки, иногда при вызове этого метода карточки будут располагаться друг над другом, а иногда они не будут

Я приложил код ниже, и любые указанные c комментарии приветствуются - особенно если ты думаешь, что я почти у цели. Но я думаю, что я действительно ищу большой дизайн изображения здесь. Я на правильном пути?

public class CardStackMoveCallback extends ItemTouchHelper.SimpleCallback {

    public interface CardStackMoveContract {
        void onCardStackSelectedForMove(StackViewHolder stackViewHolder);
        void onCardStackFinishedWithMove(StackViewHolder stackViewHolder);
        void onCardStackMoved(StackViewHolder moved, int to);
        void onCardStacksMerged(StackViewHolder dragged, StackViewHolder target);
    }

    private static final String TAG = CardStackMoveCallback.class.getSimpleName();

    public static CardStackMoveCallback create(CardStackMoveContract adapter) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;

        CardStackMoveCallback callback = new CardStackMoveCallback(dragFlags, 0);
        callback.setAdapter(adapter);
        return callback;
    }

    private CardStackMoveContract mAdapter;
    private StackViewHolder mCurrentStackToMergeInto;

    private CardStackMoveCallback(int dragDirs, int swipeDirs) {
        super(dragDirs, swipeDirs);

        mCurrentStackToMergeInto = null;
    }

    private void setAdapter(CardStackMoveContract adapter) {
        mAdapter = adapter;
    }

    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder,
                          @NonNull RecyclerView.ViewHolder target) {
        if (viewHolder instanceof StackViewHolder) {
            mAdapter.onCardStackMoved((StackViewHolder) viewHolder, target.getAdapterPosition());
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return false;
    }

    @Override
    public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
        if (viewHolder instanceof  StackViewHolder) {
            mAdapter.onCardStackSelectedForMove((StackViewHolder) viewHolder);
        }

        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        if (viewHolder instanceof  StackViewHolder) {
            StackViewHolder stackViewHolder = (StackViewHolder) viewHolder;
            Log.v(TAG, viewHolder + " & " + mCurrentStackToMergeInto + " intersect? " + viewHoldersIntersect(viewHolder, mCurrentStackToMergeInto));

            if (viewHoldersIntersect(stackViewHolder, mCurrentStackToMergeInto)) {
                mAdapter.onCardStacksMerged(stackViewHolder, mCurrentStackToMergeInto);
            } else {
                mAdapter.onCardStackFinishedWithMove(stackViewHolder);
            }
        }
        super.clearView(recyclerView, viewHolder);
    }

    @Override
    public RecyclerView.ViewHolder chooseDropTarget(@NonNull RecyclerView.ViewHolder selected, @NonNull List<RecyclerView.ViewHolder> dropTargets, int curX, int curY) {
        Log.v(TAG, "selected: " + selected + "; dropTargets: " + dropTargets);
        StackViewHolder selectedStackViewHolder = (StackViewHolder) selected;

        if (mCurrentStackToMergeInto == null && !selectedStackViewHolder.isPrayerPack()) {
            Log.v(TAG, "mCurrentStackToMergeInto is null so we're checking for A NEW CONTENDER");
            for (RecyclerView.ViewHolder viewHolder : dropTargets) {
                if (viewHolder instanceof StackViewHolder && viewHoldersIntersect(selected, viewHolder)) {
                    StackViewHolder toMergeWith = (StackViewHolder) viewHolder;

                    if (!toMergeWith.isPrayerPack()) {
                        mCurrentStackToMergeInto = toMergeWith;
                        selectedStackViewHolder.startMerge();
                        mCurrentStackToMergeInto.startMergeInto();
                        break;
                    }
                }

            }
        }

        return super.chooseDropTarget(selected, dropTargets, curX, curY);
    }

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

        if (mCurrentStackToMergeInto != null && !viewHoldersIntersect(viewHolder, mCurrentStackToMergeInto)) {
            Log.v(TAG, "Merge cancelled");
            StackViewHolder dragged = (StackViewHolder) viewHolder;
            dragged.cancelMerge();
            mCurrentStackToMergeInto.cancelMergingInto();
            mCurrentStackToMergeInto = null;
        }
    }

    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) { }

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

    private boolean viewHoldersIntersect(RecyclerView.ViewHolder dragged, RecyclerView.ViewHolder target) {
        if (target == null) {
            return false;
        }

        Rect draggedRect = new Rect();
        Rect targetRect = new Rect();
        dragged.itemView.getHitRect(draggedRect);
        target.itemView.getHitRect(targetRect);
        Log.v(TAG, "draggedRect: " + draggedRect + "; targetRect: " + targetRect);

        return Rect.intersects(draggedRect, targetRect);
    }
}


...