9

As question states. here is my dragshadowbuilder for reference. As you know, the shadow that is created when you drag is translucent. Is there anyway to make it opaque? (fully solid)

public class MyDragShadowBuilder extends View.DragShadowBuilder {
    View view;
    int width;
    int height;
    Bitmap bitmap;
    public MyDragShadowBuilder(View v) {
        super(v);
        view = v;
        ImageView b = (ImageView)view;
        int x = Character.getNumericValue(b.getContentDescription().charAt(0));
        int y = Character.getNumericValue(b.getContentDescription().charAt(1));
        bitmap = InGameActivity.currentBoardState[x][y].getPicture();
        width = bitmap.getWidth();
        height = bitmap.getHeight();
    }

    @Override
    public void onDrawShadow(Canvas canvas) {
        Rect source = new Rect(0, 0, InGameActivity.chessSquareHeightWidth, InGameActivity.chessSquareHeightWidth);
        canvas.drawBitmap(bitmap, null, source, null);
    }

    @Override
    public void onProvideShadowMetrics(Point shadowSize, Point touchPoint) {
        shadowSize.set(InGameActivity.chessSquareHeightWidth, InGameActivity.chessSquareHeightWidth);
        touchPoint.set(InGameActivity.chessSquareHeightWidth/2, InGameActivity.chessSquareHeightWidth/2);
    }
}

EDIT: Here is my current onTouch code, I'll also provide a background of the app.

OnTouchListener myOnTouchListener = new OnTouchListener() {

                @Override
                public boolean onTouch(View view, MotionEvent event) {
                    System.out.println(event.toString());
                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        ImageView b = (ImageView)view;
                        int x = Character.getNumericValue(b.getContentDescription().charAt(0));
                        int y = Character.getNumericValue(b.getContentDescription().charAt(1));
                        if (!currentBoardState[x][y].isEmpty()) {
                            ((ImageView)view).setImageBitmap(null);
                            DragShadowBuilder shadowBuilder = new MyDragShadowBuilder(view);  
                            view.startDrag(null, shadowBuilder, view, 0);
                        }

                        moveChessPiece;
                        return true;
                    }

                    return false;
                }
            });

Ok so here's what you need to know. I am developing a chess application. I have an 8x8 GridLayout which I have named currentBoardState. So currently I want to be able to drag the piece, which works, but I want the dragged piece to be opaque so the user gets the feeling that he/she is actually MOVING the piece rather than just clicking then clicking on a destination as I previously had it.

Ogen
  • 6,499
  • 7
  • 58
  • 124

5 Answers5

16

Dont know if someone is aware of it or not, We can achieve the same using flag DRAG_FLAG_OPAQUE, So the correspondind startDrag method will look like as below:

    View.DragShadowBuilder dragShadowBuilder = new View.DragShadowBuilder(view);
    view.startDrag(clipData, dragShadowBuilder, view, DRAG_FLAG_OPAQUE);
Kaleem Patel
  • 221
  • 2
  • 8
11

As @Ernir says, there no API get DragShadow. But we know the drag location and drag view so we can draw it ourselves.

I write a simple and function limited demo you can download it here.

It implement a custom ViewGroup named DragContainer, Use it to wrap your original root view in your layout xml.

<info.dourok.android.demo.DragContainer xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- original root view -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
         <!-- ... -->
    </RelativeLayout>

</info.dourok.android.demo.DragContainer>

call DragContainer#startDragChild to start drag like that(the demo have more detail):

in Activity:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDragContainer = (DragContainer) findViewById(R.id.root);
        View drag = mDragContainer.findViewById(R.id.drag);
        View drop = mDragContainer.findViewById(R.id.drop);
        drag.setTag(IMAGEVIEW_TAG);
        drag.setOnLongClickListener(new View.OnLongClickListener() {
            public boolean onLongClick(View v) {
                ClipData.Item item = new ClipData.Item((CharSequence) v
                        .getTag());
                ClipData dragData = new ClipData((CharSequence) v.getTag(),
                        new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN },
                        item);
                return mDragContainer.startDragChild(v, dragData, // the data to
                                                                    // be
                                                                    // dragged
                        null, // no need to use local data
                        0 // flags (not currently used, set to 0)
                        );

            }
        });


    }

You also can extend DragContainer to draw custom drag shadow, This is a example for your requirement.

public class MyDragContainer extends DragContainer {
    public MyDragContainer(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyDragContainer(Context context) {
        super(context, null);
    }

    public MyDragContainer(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    int width;
    int height;
    Bitmap bitmap;

    @Override
    protected void onSetDragTarget(View v) {
        ImageView b = (ImageView) v;
        int x = Character.getNumericValue(b.getContentDescription().charAt(0));
        int y = Character.getNumericValue(b.getContentDescription().charAt(1));
        bitmap = InGameActivity.currentBoardState[x][y].getPicture();
        width = bitmap.getWidth();
        height = bitmap.getHeight();
    }

    @Override
    protected void drawDragShadow(Canvas canvas) {
        Rect source = new Rect(0, 0, InGameActivity.chessSquareHeightWidth, InGameActivity.chessSquareHeightWidth);

        int w =  InGameActivity.chessSquareHeightWidth;
        int h = InGameActivity.chessSquareHeightWidth;

        canvas.translate(getDragX() - w / 2, getDragY() - h / 2);

        canvas.drawBitmap(bitmap, null, source, null);
    }
}

I hope it will help.

DouO
  • 4,914
  • 1
  • 20
  • 29
  • Hi, @Clay have you run the demo? – DouO Jan 16 '14 at 13:06
  • Hi @Clay, I have update the demo you need to download it again. – DouO Jan 16 '14 at 13:33
  • You must declare `DragContainer` in your activity layout xml as root view and get the reference after `setContentView`. for example: `mDragContainer = (DragContainer) findViewById(R.id.root);` – DouO Jan 17 '14 at 00:51
  • `DragContainer` is not a `DragShadowBuilder`. It's a `Viewgroup`, A parent of your original activity view. You can contact me via gtalk. – DouO Jan 17 '14 at 00:58
3

I think the proper way to achieve this is:

/* Create the shadow from a view */
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
/* Access the view created and set the alpha of it to 1 (totally opaque)*/
shadowBuilder.getView().setAlpha(1);
/* Start dragging the object */
view.startDrag(data, shadowBuilder, view, 0);

EDIT It doesn't seem to work on 4.4 and beyond :(

pleonasmik
  • 779
  • 10
  • 16
2

No, it is unfortunately not possible as far as I can tell.

The shadow Bitmap is drawn on a surface that you can't reach. The only solution is to implement Drag & Drop like it was done pre API 11. It is actually not as hard as you might think.

Some links to get you started:

Link 1

Link 2 *updated

Ernir Erlingsson
  • 2,150
  • 1
  • 19
  • 17
  • The second link you provided, does that have an opaque shadow? I can try it out but maybe you know already – Ogen Jan 16 '14 at 01:18
  • Sorry, the second link was wrong, now I just got your hopes up :/. I have edited my response. – Ernir Erlingsson Jan 16 '14 at 01:21
  • If I gave you my onTouch code, could you help me integrate it with ur second link? – Ogen Jan 16 '14 at 02:13
  • Please see my edit, if you need any other information, please don't hesitate to ask – Ogen Jan 16 '14 at 08:32
  • 2
    Did you just delegate the task over to me? I´m sorry, that won´t work. You should do this yourself, but if you run into problems I can **help** you. – Ernir Erlingsson Jan 16 '14 at 08:59
0

Its not possible to achieve that. This is how startDragAndDrop is creating the surface to drag on:

final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
            .setName("drag surface")
            .setParent(root.getSurfaceControl())
            .setBufferSize(shadowSize.x, shadowSize.y)
            .setFormat(PixelFormat.TRANSLUCENT)
            .setCallsite("View.startDragAndDrop")
            .build();

As you can see, the format is set to PixelFormat.TRANSLUCENT, and no option to change that

Corbella
  • 1,791
  • 14
  • 24