Skip to content

Instantly share code, notes, and snippets.

@yccheok
Created May 17, 2024 10:50
Show Gist options
  • Save yccheok/8e4fdf9026cfa6c9e80730dedf07a7b3 to your computer and use it in GitHub Desktop.
Save yccheok/8e4fdf9026cfa6c9e80730dedf07a7b3 to your computer and use it in GitHub Desktop.
/*
* Copyright (C) 2015 Paul Burke
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yocto.wenote.helper;
import android.graphics.Canvas;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.yocto.wenote.Utils;
import com.yocto.wenote.load_more.LoadMoreSection;
import com.yocto.wenote.message.MessageSection;
import com.yocto.wenote.recyclerview.PaddingSection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import io.github.luizgrp.sectionedrecyclerviewadapter.Section;
import io.github.luizgrp.sectionedrecyclerviewadapter.SectionedRecyclerViewAdapter;
import static com.yocto.wenote.Utils.trackEvent;
/**
* An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
* swipe-to-dismiss. Drag events are automatically started by an item long-press.<br/>
* </br/>
* Expects the <code>RecyclerView.Adapter</code> to listen for {@link
* ItemTouchHelperAdapter} callbacks and the <code>RecyclerView.ViewHolder</code> to implement
* {@link ItemTouchHelperViewHolder}.
*
* @author Paul Burke (ipaulpro)
*/
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public static final float ALPHA_FULL = 1.0f;
private final List<ItemTouchHelperAdapter> mAdapters;
private boolean longPressDragEnabled = true;
private final boolean showShadowDuringFirstPress;
public SimpleItemTouchHelperCallback(boolean showShadowDuringFirstPress, ItemTouchHelperAdapter... adapters) {
mAdapters = new ArrayList<>(Arrays.asList(adapters));
this.showShadowDuringFirstPress = showShadowDuringFirstPress;
}
public void setLongPressDragEnabled(boolean longPressDragEnabled) {
this.longPressDragEnabled = longPressDragEnabled;
}
@Override
public boolean isLongPressDragEnabled() {
return longPressDragEnabled;
}
@Override
public boolean isItemViewSwipeEnabled() {
return false;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (SectionedRecyclerViewAdapter.VIEW_TYPE_ITEM_LOADED != SectionedRecyclerViewAdapter.getSectionItemViewTypeForAdapterViewType(viewHolder.getItemViewType())) {
final int dragFlags = 0;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
final SectionedRecyclerViewAdapter sectionedRecyclerViewAdapter = (SectionedRecyclerViewAdapter)recyclerView.getAdapter();
final int adapterPosition = viewHolder.getAdapterPosition();
final int itemCount = sectionedRecyclerViewAdapter.getItemCount();
if (adapterPosition < 0 || adapterPosition >= itemCount) {
trackEvent("getMovementFlags", "fatal", adapterPosition + ">=" + itemCount);
final int dragFlags = 0;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
Section section = sectionedRecyclerViewAdapter.getSectionForPosition(adapterPosition);
if (section instanceof MessageSection || section instanceof PaddingSection || section instanceof LoadMoreSection) {
final int dragFlags = 0;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
// Set movement flags based on the layout manager
final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
// GridLayoutManager check should come before LinearLayoutManager, this is because
// LinearLayoutManager is GridLayoutManager's based class.
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
} else if (layoutManager instanceof LinearLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()) {
return false;
}
final int fromPosition = source.getAdapterPosition();
final int toPosition = target.getAdapterPosition();
// Notify the adapter of the move
for (ItemTouchHelperAdapter mAdapter : mAdapters) {
if (mAdapter.onItemMove(fromPosition, toPosition)) {
break;
}
}
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
// Notify the adapter of the dismissal
for (ItemTouchHelperAdapter mAdapter : mAdapters) {
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
// Fade out the view as it is swiped out of the parent's bounds
final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
return;
} else if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
if (isCurrentlyActive) {
if (showShadowDuringFirstPress || dY != 0.0 || dX != 0.0) {
ViewCompat.setElevation(viewHolder.itemView, Utils.dpToPixelInFloat(4));
}
} else {
if (dY == 0.0 && dX == 0.0) {
ViewCompat.setElevation(viewHolder.itemView, 0);
}
}
}
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
// We only want the active item to change
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
if (viewHolder instanceof ItemTouchHelperViewHolder) {
// Let the view holder know that this item is being moved or dragged
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemSelected();
}
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setAlpha(ALPHA_FULL);
ViewCompat.setElevation(viewHolder.itemView, 0);
if (viewHolder instanceof ItemTouchHelperViewHolder) {
// Tell the view holder it's time to restore the idle state
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment