How to create a custom shadow for friction?

What I want to achieve:

I want to create a drag and drop function in Android. I would like to use a specific layout (different from the drag object itself) as a drop shadow.

What result do I get instead:

None of my approaches work as expected - in the end I don’t have a visible shadow shadow (although the target gets dropped).

What I tried:

I tried

  • inflating the drag_item layout in activity and then passing it as an argument to the shadow builder constructor

and

  • inflating the drag_item layout in the drag_item shadow builder onDrawShadow and then drawing it on 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 the shadow 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:

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 the 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 am I doing wrong?

Update:

Added bounty.

+7
android drag-and-drop
source share
2 answers

Kurty is true that in this case you do not need to subclass DragShadowBuilder . My thought is that the view you pass to the DragShadowBuilder does not actually exist in the layout and therefore it does not appear.

Instead of passing null as the second argument to inflater.inflate , try actually adding a puffed 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 know that the dragged_item mapping, which is rendered all the time, is not what you want, but if it works, then at least we know where the problem is, and we can look for a solution for this!

+2
source share

Simply put, you only need this:

 private final class TouchListener implements View.OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { v.startDrag(ClipData.newPlainText("", ""), new View.DragShadowBuilder(v), v, 0); } return true; } } 

(You do not need the BuilderOne and BuilderTwo classes)

-one
source share

All Articles