What I want to achieve:
I want to create a drag and drop functionality in Android. I'd like to use a specific layout (different from the dragged object itself) as a drag shadow.
What result I'm getting instead:
Neither of my approaches works as expected - I end up with no visible drag shadow at all (although the target does receive the drop).
What I tried:
I tried
drag_item
layout in the activity, then passing it as an argument to the shadow builder's constructorand
drag_item
layout in the shadow builder's onDrawShadow
method, then drawing it on the canvas
Layouts:
My activity layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.app.DragDropTestActivity"
tools:ignore="MergeRootFrame">
<TextView
android:id="@+id/tvReceiver"
android:text="Drop here"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btnDragged"
android:layout_height="wrap_content"
android:text="Drag me"
android:layout_width="match_parent"/>
</LinearLayout>
The layout I want to use as a drag shadow:
dragged_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dragged Item"/>
</LinearLayout>
Source code:
Here's the code with both approaches (represented by 1
, BuilderOne
and 2
, BuilderTwo
, respectively):
package com.example.app;
import android.graphics.Canvas;
import android.graphics.Point;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
public class DragDropTestActivity extends ActionBarActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag_drop_test);
Button dragged = (Button) findViewById(R.id.btnDragged);
dragged.setOnTouchListener(
new View.OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent event)
{
if (event.getAction() != MotionEvent.ACTION_DOWN) {
return false;
}
LayoutInflater inflater = getLayoutInflater();
int approach = 1;
// both approaches fail
switch (approach) {
case 1: {
View draggedItem = inflater.inflate(R.layout.dragged_item, null);
BuilderOne builder = new BuilderOne(draggedItem);
v.startDrag(null, builder, null, 0);
break;
}
case 2: {
BuilderTwo builder = new BuilderTwo(inflater, v);
v.startDrag(null, builder, null, 0);
break;
}
}
return true;
}
});
}
My BuilderOne
class:
public static class BuilderOne extends View.DragShadowBuilder
{
public BuilderOne(View view)
{
super(view);
}
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint)
{
super.onProvideShadowMetrics(
shadowSize,
shadowTouchPoint);
}
}
And BuilderTwo
class:
public static class BuilderTwo extends View.DragShadowBuilder
{
final LayoutInflater inflater;
public BuilderTwo(LayoutInflater inflater, View view)
{
super(view);
this.inflater = inflater;
}
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint)
{
super.onProvideShadowMetrics(
shadowSize,
shadowTouchPoint);
}
@Override
public void onDrawShadow(Canvas canvas)
{
final View draggedItem = inflater.inflate(R.layout.dragged_item, null);
if (draggedItem != null) {
draggedItem.draw(canvas);
}
}
}
}
Question:
What do I do wrong?
Update:
Bounty added.
To create shadows, we have to use the free transform tool by keeping the duplicate layer selected press Ctrl+t to bring the transform tool. With the tool, you can move the black portion anywhere you want.
The drop shadow needs to be stretched out, so it appears more realistic. The next step is changing that shadow into a realistic shadow. Change the drop shadow into a new layer. Now you can manipulate it on its own. Follow these steps to make this happen: On the layer containing your shadow. Right-click on the fx symbol that has now appeared.
Shape Your Shadow 1 In the layer with your object, add a Drop Shadow Effect (fx icon). ... 2 Right-click on the fx symbol that has now appeared in your layer and select Create Layer. ... 3 Use the transform tools described above (skew, scale, and warp) to adjust the shadow to make sense in the image. ... More items...
This shadow should be stronger near the base, and more diffused, lighter near the top. To do this, create a second shadow layer. Select your shadow layer and go to Layer>Duplicate Layer (Ctrl or ⌘J). You now have a copy. Click on the first shadow layer. Enter 90% into the Fill box found near the top of the layers window.
Kurty is correct in that you shouldn't need to subclass DragShadowBuilder
in this case. My thought is that the view you're passing to the DragShadowBuilder
doesn't actually exist in the layout, and therefore it doesn't render.
Rather than passing null
as the second argument to inflater.inflate
, try actually adding the inflated View
to the hierarchy somewhere, and then passing it to a regular DragShadowBuilder
:
View dragView = findViewById(R.id.dragged_item);
mDragShadowBuilder = new DragShadowBuilder(dragView);
v.startDrag(null, mDragShadowBuilder, null, 0);
EDIT
I'm aware that having the dragged_item
view being rendered all the time isn't what you want, but if it works then at least we know where the problem is and can look for a solution to that instead!
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