I have a list view with different item types: header, folder and file like this:
Now I 'd like to implement drag file item and drop it into folder item and get the source and target position.I don't want to change the target position (rearrange) while dragging like some drag sort list view libraries.
Is there any suggestion to start with?
Switching your ListView
to RecyclerView
will make things a lot easier.
You can find the whole article on Styling Android and the whole code here.
This code uses OnItemTouchListener
to detect when an item should be dragged. There is an ImageView
above the RecyclerView
with an image of the item being moved to cheaply animate it.
The OnItemTouckListener
(DragController.java
):
public class DragController implements RecyclerView.OnItemTouchListener {
private RecyclerView recyclerView;
private ImageView overlay;
private final GestureDetectorCompat gestureDetector;
private boolean isDragging = false;
public DragController(RecyclerView recyclerView, ImageView overlay) {
this.recyclerView = recyclerView;
this.overlay = overlay;
GestureDetector.SimpleOnGestureListener longClickGestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent e) {
super.onLongPress(e);
isDragging = true;
dragStart(e.getX(), e.getY());
}
};
this.gestureDetector = new GestureDetectorCompat(recyclerView.getContext(), longClickGestureListener);
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if (isDragging) {
return true;
}
gestureDetector.onTouchEvent(e);
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
int x = (int) e.getX();
int y = (int) e.getY();
View view = recyclerView.findChildViewUnder(x, y);
if (e.getAction() == MotionEvent.ACTION_UP) {
dragEnd(view);
isDragging = false;
} else {
drag(y, view);
}
}
Starting and ending the drag (DragController.java
):
private boolean isFirst = true;
private static final int ANIMATION_DURATION = 100;
private int draggingItem = -1;
private float startY = 0f;
private Rect startBounds = null;
private void dragStart(float x, float y) {
View draggingView = recyclerView.findChildViewUnder(x, y);
View first = recyclerView.getChildAt(0);
isFirst = draggingView == first;
startY = (y - draggingView.getTop());
paintViewToOverlay(draggingView);
overlay.setTranslationY(y - startY);
draggingView.setVisibility(View.INVISIBLE);
draggingItem = recyclerView.indexOfChild(draggingView);
startBounds = new Rect(draggingView.getLeft(), draggingView.getTop(), draggingView.getRight(), draggingView.getBottom());
}
private void drag(int y, View view) {
overlay.setTranslationY(y - startY);
}
private void dragEnd(View view) {
overlay.setImageBitmap(null);
view.setVisibility(View.VISIBLE);
view.setTranslationY(overlay.getTranslationY() - view.getTop());
view.animate().translationY(0f).setDuration(ANIMATION_DURATION).start();
}
private void paintViewToOverlay(View view) {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
overlay.setImageBitmap(bitmap);
overlay.setTop(0);
}
The code is written by Mark Allison on StylingAndroid.
Edit:
But I don't know how to get the position of item when dragging is end
The answer is located in part 7 on Styling Android.
View view = recyclerView.findChildViewUnder(0, y);
And how can I disable drag on Folder and Header item? Just allow dragging File item?
You can do this by using multiple ViewTypes (file, folder & header). Then you can use getItemViewType in DragController
to start the movement only for files.
Use RecyclerView
and ItemTouchHelper.SimpleCallback
.
You can setup it like that in your activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list); // Your layout with RecyclerView
RecyclerView itemRecyclerView = findViewById(R.id.itemRecyclerView);
LinearLayoutManager itemLayoutManager = new LinearLayoutManager(this);
itemRecyclerView.setLayoutManager(itemLayoutManager);
itemAdapter = new ItemAdapter(); // Your adapter which extends RecyclerView.Adapter
itemRecyclerView.setAdapter(itemAdapter);
itemRecyclerView.setHasFixedSize(true);
itemDragAndDropCallback = new ItemDragAndDropCallback(this, itemRecyclerView);
// Your class which extends ItemTouchHelper.SimpleCallback
// It will be shown in the next code sample
new ItemTouchHelper(itemDragAndDropCallback)
.attachToRecyclerView(itemRecyclerView);
}
You can use default functionality for item dragging provided by ItemTouchHelper.SimpleCallback
.
The following class will demonstrate changing the background color of a folder. An item will be dropped into that folder.
class ItemDragAndDropCallback extends ItemTouchHelper.SimpleCallback {
ItemDragAndDropCallback() {
// Choose drag and swipe directions
// Up and down is chosen for dragging
// Right and left is chosen for swiping
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
// You can reorder items here
// Do nothing in your case
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
// You can react for swiping items here
// Do nothing in your case
}
// An item will be dropped into this folder
private View folder;
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
// Here you are notified that the drag operation began
if (folder != null) {
folder.setBackgroundResource(0); // Clear former folder background
}
} else if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) {
// Here you are notified that the last operation ended
if (folder != null) {
// Set folder background to a color indicating
// that an item was dropped into it
folder.setBackgroundColor(
ContextCompat.getColor(
recyclerView.getContext(), android.R.color.holo_green_dark
)
);
}
}
}
@Override
public void onChildDraw(
Canvas c,
RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
float dX,
float dY,
int actionState,
boolean isCurrentlyActive
) {
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && isCurrentlyActive) {
// Here you are notified that the drag operation is in progress
if (folder != null) {
folder.setBackgroundResource(0); // Clear former folder background
}
float itemActualPosition = viewHolder.itemView.getTop() + dY;
// Find folder under dragged item
for (int i = 0; i < recyclerView.getChildCount(); i++) {
folder = recyclerView.getChildAt(i);
// Exclude dragged item from detection
if (!folder.equals(viewHolder.itemView)) {
// Accept folder which encloses item position
if (folder.getTop() < itemActualPosition && itemActualPosition < folder.getBottom()) {
// Set folder background to a color indicating
// that an item will be dropped into it upon release
folder.setBackgroundColor(
ContextCompat.getColor(
recyclerView.getContext(), android.R.color.holo_green_light
)
);
break;
}
}
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
When you drag an item over folders then the folder's background under the item will be light green. When you drop the item into a folder then its background will be dark green.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With