Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I am new to android development and i want to create an app for drag and drop functionality.The condition is when i drag an item until we drop that item at any position the position of that dragged view must be empty.
I solved same problem by ItemTouchHelper. Its little but tricky but simple after understand. Suppose you have a recycleView called mRecyclerView.
public void initRecycleView(){
//initiate your recycleView here ....
mRecyclerView.setAdapter(your_recycleView_adapter));
ItemTouchHelper touchHelper = new ItemTouchHelper(rvCallback);
touchHelper.attachToRecyclerView(mRecyclerView);
}
int dragStartFromPosition = -1;
ItemTouchHelper.Callback rvCallback = new ItemTouchHelper.Callback() {
int dragFromPosition = -1;
int dragToPosition = -1;
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
//final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, 0);
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
if (dragFromPosition == -1) { //For Saving drag start from position
dragStartFromPosition = viewHolder.getAdapterPosition();
}
dragFromPosition = viewHolder.getAdapterPosition();
dragToPosition = target.getAdapterPosition();
moveItem(dragFromPosition, dragToPosition);
dragFromPosition = dragToPosition;
dragToPosition = -1;
return true;
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (dragFromPosition != -1 && dragToPosition != -1 && dragFromPosition != dragToPosition) {
finalMoved(dragFromPosition, dragToPosition);
} else {
moveItem(dragFromPosition, dragStartFromPosition);
dragStartFromPosition = -1;
}
dragFromPosition = dragToPosition = -1;
}
private void finalMoved(int from, int to) {
moveAlertDialog(from, to);
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
#Override
public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
};
private void moveItem(int from, int to) {
Collections.swap(itemArrayList, from, to);
rvAdapter.notifyItemMoved(from, to);
}
public void moveAlertDialog(final int from, final int to) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Do you want to move this item?");
builder.setPositiveButton("move", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dragStartFromPosition = -1;
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
moveItem(from, dragStartFromPosition);
dragStartFromPosition = -1;
}
});
builder.show();
}
Feel free to ask question if you face any problem.
Thanks
Related
I want to write a piece of code into where item drag-drop is over. I got this answer https://stackoverflow.com/a/36275415/1684778 but I am confused about how to achieve this(where i have to implement that answer)
I want to write onDrop instead of onItemMove, how to achieve it.
DragItemTouchHelper
public class DragItemTouchHelper extends ItemTouchHelper.Callback {
public static final float ALPHA_FULL = 1.0f;
private final MoveHelperAdapter mAdapter;
public DragItemTouchHelper(MoveHelperAdapter adapter) {
mAdapter = adapter;
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return false;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
// Set movement flags based on the layout manager
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()) {
return false;
}
// Notify the adapter of the move
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean 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);
} else {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
#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 TouchViewHolder) {
// Let the view holder know that this item is being moved or dragged
TouchViewHolder itemViewHolder = (TouchViewHolder) 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);
if (viewHolder instanceof TouchViewHolder) {
// Tell the view holder it's time to restore the idle state
TouchViewHolder itemViewHolder = (TouchViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}
public interface MoveHelperAdapter {
boolean onItemMove(int fromPosition, int toPosition);
}
public interface TouchViewHolder {
void onItemSelected();
void onItemClear();
}
}
You can put your class that extends ItemTouchHelper callback in the end of the activity class where your RecyclerView is and attach it to RecyclerView defined in activity or fragment as below
ItemTouchHelper touchHelper = new ItemTouchHelper(your_custom_callback);
touchHelper.attachToRecyclerView(recyclerView);
I want to perform only the right swipe on RecyclerView.
But it swipes on both sides and also an upside.
How to disable swipe left and up?
Want to perform delete functionality on the right swipe.
It works but it swipes all the side of items. How to prevent this?
NotificationFragment.java
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position) {
if (viewHolder instanceof NotificationListAdapter.ViewHolder) {
mAdapter.removeItem(viewHolder.getAdapterPosition());
}
}
NotificationListAdapter.java
public void removeItem(int position) {
callDeleteService(position);
}
RecyclerItemTouchHelper
public class RecyclerItemTouchHelper extends ItemTouchHelper.SimpleCallback {
private RecyclerItemTouchHelperListener listener;
public RecyclerItemTouchHelper(int dragDirs ,int swipeDirs ,RecyclerItemTouchHelperListener listener) {
super( dragDirs ,swipeDirs );
this.listener = listener;
}
#Override
public boolean onMove(RecyclerView recyclerView ,RecyclerView.ViewHolder viewHolder ,RecyclerView.ViewHolder target) {
return true;
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder ,int actionState) {
if (viewHolder != null) {
final View foregroundView = ((NotificationListAdapter.ViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onSelected( foregroundView );
}
}
#Override
public void onChildDrawOver(Canvas c ,RecyclerView recyclerView ,
RecyclerView.ViewHolder viewHolder ,float dX ,float dY ,
int actionState ,boolean isCurrentlyActive) {
final View foregroundView = ((NotificationListAdapter.ViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onDrawOver( c ,recyclerView ,foregroundView ,dX ,dY ,
actionState ,isCurrentlyActive );
}
#Override
public void clearView(RecyclerView recyclerView ,RecyclerView.ViewHolder viewHolder) {
final View foregroundView = ((NotificationListAdapter.ViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().clearView( foregroundView );
}
#Override
public void onChildDraw(Canvas c ,RecyclerView recyclerView ,
RecyclerView.ViewHolder viewHolder ,float dX ,float dY ,
int actionState ,boolean isCurrentlyActive) {
final View foregroundView = ((NotificationListAdapter.ViewHolder) viewHolder).viewForeground;
getDefaultUIUtil().onDraw( c ,recyclerView ,foregroundView ,dX ,dY ,
actionState ,isCurrentlyActive );
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder ,int direction) {
// if (direction == ItemTouchHelper.END){
listener.onSwiped( viewHolder ,direction ,viewHolder.getAdapterPosition() );
// }
// else {
// Log.d( "Swipe LEFT : ", " DO NOTHING" );
// }
}
#Override
public int getMovementFlags(RecyclerView recyclerView ,RecyclerView.ViewHolder viewHolder) {
int dragFlags = 0;
int swipeFlags = ItemTouchHelper.LEFT;
return makeMovementFlags( dragFlags ,swipeFlags );
}
#Override
public int convertToAbsoluteDirection(int flags ,int layoutDirection) {
return super.convertToAbsoluteDirection( flags ,layoutDirection );
}
public interface RecyclerItemTouchHelperListener {
void onSwiped(RecyclerView.ViewHolder viewHolder ,int direction ,int position);
}
}
I want it like a 1st image.
We can define swipe directions by using SimpleCallback().
For RIGHT:
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.RIGHT, this);
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);
For LEFT:
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT, this);
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);
For Both:
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);
I wrote a RecyclerView that the item in it can be dragged. When I dragged item moving random. Then I see the data, the order of the data in the adapter and the order of the data showed in the RecyclerView are not same? Why, who can help me?
The code is below:
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlag, swipeFlag;
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
dragFlag = ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
swipeFlag = 0;
} else {
dragFlag = ItemTouchHelper.DOWN | ItemTouchHelper.UP;
swipeFlag = ItemTouchHelper.END;
}
return makeMovementFlags(dragFlag, swipeFlag);
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int fromPosi = viewHolder.getAdapterPosition();
int toPosi = target.getAdapterPosition();
adapter.move(fromPosi, toPosi);
EventBus.getDefault().post(new EventMessage(ModifyMainMenuActivity.class.getSimpleName()));
return true;
}
****and the adapter is followed***
public class ShopDecorationAdapter extends
BaseRecyclerViewAdapter<ProductListResponse.ProductItem> {
private int imageHeight, imageWidth;
public ShopDecorationAdapter(Context context, int layout, OnItemClickListener onItemClickListener) {
super(context, layout, onItemClickListener);
}
public void setImageSize(int imgH, int imgW) {
this.imageHeight = imgH;
this.imageWidth = imgW;
}
#Override
protected void bindData(BaseViewHolder holder, ProductListResponse.ProductItem data, int position) {
holder.getTextView(R.id.tv_product_name).setText(data.name);
ViewGroup.LayoutParams imgLayoutParam = holder.getImageView(R.id.product_icon).getLayoutParams();
imgLayoutParam.height = imageHeight;
imgLayoutParam.width = imageWidth;
holder.getItemView().setLayoutParams(imgLayoutParam);
holder.getImageView(R.id.product_icon).setLayoutParams(imgLayoutParam);
if (data.isAddBtn) {
holder.getTextView(R.id.tv_product_name).setGravity(Gravity.CENTER_HORIZONTAL);
// ImgLoadUtils.loadLocalResourceDrawable(mContext, R.drawable.icon_add_in_teaching_record,
// holder.getImageView(R.id.product_icon));
holder.getImageView(R.id.product_icon).setImageResource(R.drawable.icon_add_in_teaching_record);
holder.getTextView(R.id.tv_product_price).setVisibility(View.GONE);
} else {
holder.getTextView(R.id.tv_product_price).setVisibility(View.VISIBLE);
holder.getTextView(R.id.tv_product_price).setText(mContext.getString(R.string.im_product_price, data.zhimaPrice));
ImgLoadUtils.loadIntoUseFitWith(mContext, R.drawable.artisan_icon_miss, EncodeUtils.urlString(data.coverPic)
, holder.getImageView(R.id.product_icon));
if (data.isSelected && !data.isAddBtn) {
holder.getItemView().setBackgroundResource(R.drawable.bg_cf34270_border1dp_solid_white);
} else {
holder.getItemView().setBackgroundResource(R.drawable.bg_white);
}
}
}
public void move(int origin, int target) {
Collections.swap(datas, origin, target);
if (origin < target) {
for (int i = origin; i < target; i++) {
Collections.swap(datas, i, i + 1);
}
}
if (origin > target) {
for (int i = origin; i > target; i--) {
Collections.swap(datas, i, i - 1);
}
}
notifyItemMoved(origin, target);
}
}
Remove unnecessary swap
public void move(int origin, int target) {
//Collections.swap(datas, origin, target); !!!!!!!!
if (origin < target) {
for (int i = origin; i < target; i++) {
Collections.swap(datas, i, i + 1);
}
}
if (origin > target) {
for (int i = origin; i > target; i--) {
Collections.swap(datas, i, i - 1);
}
}
notifyItemMoved(origin, target);
}
Go to File->Settings->Build, Execution, Deployment->Gradle->Uncheck Offline work option.
I want to implement stack of cards from bottom to top swipe each overlap previous card stack.Someone implement this using RecyclerView with custom Layout manager.I don't have enough code.
How to do this using Recyclerview.
Try this way:
public class StackLayoutManager extends LinearLayoutManager {
public StackLayoutManager(Context context) {
super(context);
setStackFromEnd(true);
}
#Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
#Override
public boolean canScrollHorizontally() {
return false;
}
#Override
public boolean canScrollVertically() {
return false;
}
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
updatePosition();
}
private void updatePosition() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
ViewCompat.setTranslationY(view, -view.getTop());
}
}
}
Than use it with your RecyclerView:
recyclerView.setLayoutManager(new StackLayoutManager(this));
And finally add ItemTouchHelper for handling swiping:
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
int pos = viewHolder.getAdapterPosition();
if (pos != RecyclerView.NO_POSITION) {
adapter.onRemove(viewHolder);
}
}
});
itemTouchHelper.attachToRecyclerView(recyclerView);
public class ItemCallBack extends ItemTouchHelper.Callback {
private BaseDragAdapter adapter;
private List list;
public ItemCallBack(BaseDragAdapter adapter, List list) {
this.adapter = adapter;
this.list = list;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = 0;
int swipeFlags = 0;
int startPosition = viewHolder.getAdapterPosition();
if (startPosition != 0) {
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
} else if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
} else if (recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
}
return makeMovementFlags(dragFlags, swipeFlags);
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int startPosition = viewHolder.getAdapterPosition();
int targetPosition = target.getAdapterPosition();
adapter.onMoved(startPosition, targetPosition);
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { //左右拖动删除
int position = viewHolder.getAdapterPosition();
adapter.onSwiped(position);
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return false;
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
viewHolder.itemView.setScaleX(1.2f);
viewHolder.itemView.setScaleY(1.2f);
}
super.onSelectedChanged(viewHolder, actionState);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setScaleX(1.0f);
viewHolder.itemView.setScaleY(1.0f);
onOrderListener.order();
}
#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);
}
private OnOrderListener onOrderListener;
public void setOnPictureClickListener(OnOrderListener onOrderListener) {
if (onOrderListener != null) {
this.onOrderListener = onOrderListener;
}
}
public interface OnOrderListener {
void order();
}
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_ADD;
} else if (position == 1) {
return TYPE_COVER;
}
return TYPE_NORMAL;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ADD) {
View view = LayoutInflater.from(context).inflate(R.layout.item_upload_pic_add, null);
return new AddHolder(view);
} else if (viewType == TYPE_COVER) {
View view = LayoutInflater.from(context).inflate(R.layout.item_upload_pic_cover, null);
return new CoverHolder(view);
} else {
View view = LayoutInflater.from(context).inflate(R.layout.item_upload_pic_normal, null);
return new NormalHolder(view);
}
}
I wanna swap item with ItemTouchHelper.
The issue is:
When i drag and swap item that viewtype is TYPE_NORNAL,animation work well;
But When i try to switch between different types(ex:TYPE_NORMAL and TYPE_COVER),animation does not execute correctly.
How to make the animation execute correctly?
I had the same issue. Resoved it with setting
setHasStableIds(true);
on adapter, and overriding getItemId() so that it returns unique id for each item I have in adapter. If you do not have unique id for items you can spawn Map and generate these id's when you get new items for adapter. Then lookup these id's in getItemId(), using item as a key.
Override getMovementFlags and set your swipeFlags depends of your viewType
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val swipeFlags = if (viewHolder.itemViewType == ListItem.TYPE_BODY)
ItemTouchHelper.RIGHT
else 0
return makeMovementFlags(0, swipeFlags)
}