Wednesday, April 4, 2012

Passing object in Drap And Drop using myLocalState

Last exercise demonstrate the basic logic flow of Drap And Drop operation. We can pass object from the Drap And Drop source to the target using myLocalState. In this exercise, the background and text of the target dropped button will be changed according to the drapped button.

Passing object in Drap And Drop using myLocalState

myLocalState is the third parameter in startDrag():
boolean startDrag (ClipData data, View.DragShadowBuilder shadowBuilder, Object myLocalState, int flags)
where:
  • data:
    A ClipData object pointing to the data to be transferred by the drag and drop operation.
  • shadowBuilder:
    A View.DragShadowBuilder object for building the drag shadow.
  • myLocalState:
    An Object containing local data about the drag and drop operation. This Object is put into every DragEvent object sent by the system during the current drag.
    myLocalState is a lightweight mechanism for the sending information from the dragged View to the target Views. For example, it can contain flags that differentiate between a a copy operation and a move operation.
  • flags:
    Flags that control the drag and drop operation. No flags are currently defined, so the parameter should be set to 0.


In the View.OnDragListener, we can retrieve myLocalState by calling (View)event.getLocalState().

Modify the Java code in the exercise "Introduce Drag And Drop for Android 3.0+":
- Modify calling startDrag() in sourceButtonsLongClickListener to pass myLocalState, in the third parameter.
- Modify case DragEvent.ACTION_DROP in MyDragEventListener to update Drop view from myLocalState.
package com.exercise.com;

import android.app.Activity;
import android.content.ClipData;
import android.content.ClipDescription;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.widget.Button;
import android.widget.TextView;

public class AndroidDragAndDropActivity extends Activity {

Button button1, button2, button3, button4, buttonTarget;
Button dragSourceButton;
TextView comments;

String commentMsg;

MyDragEventListener myDragEventListener = new MyDragEventListener();

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button1 = (Button)findViewById(R.id.button1);
button2 = (Button)findViewById(R.id.button2);
button3 = (Button)findViewById(R.id.button3);
button4 = (Button)findViewById(R.id.button4);
buttonTarget = (Button)findViewById(R.id.buttonTarget);
comments = (TextView)findViewById(R.id.comments);

// Create and set the tags for the Buttons
final String BUTTON1_TAG = "button1";
final String BUTTON2_TAG = "button2";
final String BUTTON3_TAG = "button3";
final String BUTTON4_TAG = "button4";
button1.setTag(BUTTON1_TAG);
button2.setTag(BUTTON2_TAG);
button3.setTag(BUTTON3_TAG);
button4.setTag(BUTTON4_TAG);

button1.setOnLongClickListener(sourceButtonsLongClickListener);
button2.setOnLongClickListener(sourceButtonsLongClickListener);
button3.setOnLongClickListener(sourceButtonsLongClickListener);
button4.setOnLongClickListener(sourceButtonsLongClickListener);

button1.setOnDragListener(myDragEventListener);
button2.setOnDragListener(myDragEventListener);
button3.setOnDragListener(myDragEventListener);
button4.setOnDragListener(myDragEventListener);
buttonTarget.setOnDragListener(myDragEventListener);
}

Button.OnLongClickListener sourceButtonsLongClickListener
= new Button.OnLongClickListener(){

@Override
public boolean onLongClick(View v) {
ClipData.Item item = new ClipData.Item((CharSequence)v.getTag());

String[] clipDescription = {ClipDescription.MIMETYPE_TEXT_PLAIN};
ClipData dragData = new ClipData((CharSequence)v.getTag(),
clipDescription,
item);
DragShadowBuilder myShadow = new MyDragShadowBuilder(v);

v.startDrag(dragData, //ClipData
myShadow, //View.DragShadowBuilder
v, //Object myLocalState
0); //flags

commentMsg = v.getTag() + " : onLongClick.\n";
comments.setText(commentMsg);

return true;
}};

private static class MyDragShadowBuilder extends View.DragShadowBuilder {
private static Drawable shadow;

public MyDragShadowBuilder(View v) {
super(v);
shadow = new ColorDrawable(Color.LTGRAY);
}

@Override
public void onProvideShadowMetrics (Point size, Point touch){
int width = getView().getWidth();
int height = getView().getHeight();

shadow.setBounds(0, 0, width, height);
size.set(width, height);
touch.set(width / 2, height / 2);
}

@Override
public void onDrawShadow(Canvas canvas) {
shadow.draw(canvas);
}

}

protected class MyDragEventListener implements View.OnDragListener {

@Override
public boolean onDrag(View v, DragEvent event) {
final int action = event.getAction();

switch(action) {
case DragEvent.ACTION_DRAG_STARTED:
//All involved view accept ACTION_DRAG_STARTED for MIMETYPE_TEXT_PLAIN
if (event.getClipDescription()
.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN))
{
commentMsg += ((Button)v).getText()
+ " : ACTION_DRAG_STARTED accepted.\n";
comments.setText(commentMsg);
return true; //Accept
}else{
commentMsg += ((Button)v).getText()
+ " : ACTION_DRAG_STARTED rejected.\n";
comments.setText(commentMsg);
return false; //reject
}
case DragEvent.ACTION_DRAG_ENTERED:
commentMsg += ((Button)v).getText() + " : ACTION_DRAG_ENTERED.\n";
comments.setText(commentMsg);
return true;
case DragEvent.ACTION_DRAG_LOCATION:
commentMsg += ((Button)v).getText() + " : ACTION_DRAG_LOCATION - "
+ event.getX() + " : " + event.getY() + "\n";
comments.setText(commentMsg);
return true;
case DragEvent.ACTION_DRAG_EXITED:
commentMsg += ((Button)v).getText() + " : ACTION_DRAG_EXITED.\n";
comments.setText(commentMsg);
return true;
case DragEvent.ACTION_DROP:
// Gets the item containing the dragged data
ClipData.Item item = event.getClipData().getItemAt(0);

commentMsg += ((Button)v).getText() + " : ACTION_DROP"
+ " - from " + item.getText().toString() + "\n";
comments.setText(commentMsg);

//If apply only if drop on buttonTarget
if(v == buttonTarget){
String droppedItem = item.getText().toString();
commentMsg += ((Button)v).getText() + " : Dropped item - "
+ droppedItem + "\n";
comments.setText(commentMsg);

//Retrieve the source view using getLocalState()
View dragView = (View)event.getLocalState();

((Button)v).setBackgroundDrawable(((Button)dragView).getBackground());
((Button)v).setText(((Button)dragView).getText());
}

return true;
case DragEvent.ACTION_DRAG_ENDED:
if (event.getResult()){
commentMsg += ((Button)v).getText() + " : ACTION_DRAG_ENDED - success.\n";
comments.setText(commentMsg);
} else {
commentMsg += ((Button)v).getText() + " : ACTION_DRAG_ENDED - fail.\n";
comments.setText(commentMsg);
};
return true;
default: //unknown case
commentMsg += ((Button)v).getText() + " : UNKNOWN !!!\n";
comments.setText(commentMsg);
return false;

}
}
}
}


Download the files.

Next:
- Drag And Drop to copy items between ListView

2 comments:

Unknown said...

Hi, Thanks so much for your code but it's work only the first time play and when I exit the app and then I play again ur code doesn't work properly, still can drag but can't drop on the position... can you please fix ur code? I am runing ur code on Android Studio...

Unknown said...

Thank you for this post. I learned from this how to pass objectstate in a drag event.