Я пытаюсь осуществить «объединение» элементов с помощью RecyclerView.
У нас есть список карточек, которые можно пролистывать, редактировать и изменять порядок. У нас также есть понятие «стопки», где вы можете объединить карты в стопку. Ранее вы могли загружать только предустановленные стеки содержимого, но сейчас я пытаюсь реализовать пользовательские стеки. Ниже приведен скриншот карт «в состоянии покоя»:
Реорганизовать процесс переупорядочения было действительно легко - я просто подключился к ItemTouchListener.Callback
, чтобы разобраться с выбором вверх и падение карт. Тем не менее, мне действительно трудно слиться с работой. Функциональность заключается в том, что когда вы отпускаете перетаскиваемую карту поверх другой карты, она должна вызывать некоторый код в другом месте, чтобы объединить эти стеки / карты в новый стек. *
- Используйте
chooseDropTarget
, чтобы сохранить ссылку на последнюю карту, над которой мы закончили - Используйте
onDraw
, чтобы уничтожить эту ссылку, если мы больше не за этой картой - Используйте
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);
}
}