Friday 13 March 2015

Drag & Drop Part1: Background Understanding

Drag and Drop framework in Android provides data movement from one view to the another. But it can be used in different scneraios also like in most of the android device while receiving a call you drag “End call image button” to the “Receive call image button” and when you place an application icon on the on of the home screen.

There are many kinds of data that draggable objects carry. Some of them are:
  • Plain Text
  • Uri
  • Intent
  • Local Object Information
Drag Event Generator & Listener

This operation involves at least two objects, one is Event generator and at least on event listener. In the below picture the left Image is event generator and in the right part two listeners are shown. The event can be generated using the long press the image. When the long press is detected by the system, this operation begin and go through different phases before the end of the operation. These phases are explained in the next sections.




Drag Actions Types

What will happens when you start a drag operation. Drag operation sends different kind of signals to the system and these signals are captured by the drag listener objects. These signals are one of six signal converted actions which are listed below and are received by the drag listener.

DRAG_STARTED

When the drag is started via long pressed image view an shadow appears which the user drags over the display area. The listener views changes its appearance for an example to indicate that it can receiver the data. Here in the below picture listener changes their background color to yellow to indicate users about its intent to receive. User can continue to drag the shadow all over the view before dropping.


    DRAG_ENTERED and DRAG_LOCATION
    Whenever the user moves the shadow and come inside the listener bound area, again listener indicates the user that now you can drop to me if you wish, I am able to receive this now by changing its background for an example.


DRAG_EXITED

If the user does not want to drop the shadow on that particular listener and get out from that view area, again that listener changes its background color to indicate user that it has exited from its bound area.



DRAG_DROPPED 
    Now, suppose the user enters into other listener view area and want to drop into that view, this listener accepts the drop and get the image data from the generator view and display it its view. Other listener get this drop information via DRAG_ENDED signal and adjusted itself to its its initial position.





    DRAG_ENDED

    This is final signal that the listener receives and if it is not the drop accepting view it re adjust itself to its initial view. Now the operation is complete.




Required Classes to perfom Drag & Drop operations

View 

A view like ImageView or TextView will start the DRAG operation and at the same time can listen the different drag events as well. These viesw are objects which are drag event generator and drag event listeners. The event generator view sets setOnLongClickListener to it to capture long press gesture. In the following snipet ImagaView generator is set like that. Likewise two ImageView listener1 and listener2 are set setOnDragListener to listen the drag events.
public class MainActivity extends Activity {

private static final String TAG = "Drag Operation";
private ImageView generator;
private ImageView listener1, listener2;
private TextView text;

OnLongClickListener longClick;
MyDragListener dragListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

generator = (ImageView) findViewById(R.id.drag_generator);
listener1 = (ImageView) findViewById(R.id.drag_listener1);
listener2 = (ImageView) findViewById(R.id.drag_listener2);
text = (TextView) findViewById(R.id.textView1);

longClick = new MyOnLongClickListener();
generator.setOnLongClickListener(longClick);

dragListener = new MyDragListener();
listener1.setOnDragListener(dragListener);
listener2.setOnDragListener(dragListener);

}
}

OnLongClickListener 

This is one of the most common source for drag event generator. Best example is Call Receiving activity. On simple click also a view can start the drag operation. In this snippet, OnLongClickListenr is extended by a custom class. The object of this custom class is used by the event generator ImageView. 

Inside the overidden method, an instance of ClipData is created. This clip data contains string information with him. A ClipData can contains various kind of Item objects like Uri, Intent, HTML text rtc. This is the primary class which is passed as first parameter of startDrag method.
class MyOnLongClickListener implements View.OnLongClickListener {

public boolean onLongClick(View v) {
listener1.setVisibility(View.VISIBLE);
listener2.setVisibility(View.VISIBLE);

ClipData clipData = ClipData.newPlainText("Image", "Picture");
//DragShadowBuilder shadowBuilder = new DragShadowBuilder(v);
DragShadowBuilder shadowBuilder = new MyDragShadow(v);

v.startDrag(clipData, shadowBuilder, v, 0);
text.setText("Drag the shadow");
return true;
}

}

In the above class definition, DragShadowBuilder object is needed as 2nd parameter of startDrag method. This object can be created directly or though some custom class that extends DragShadowBuilder class and override its methods to provide custom dimension of shadow and touch point. We will see a lot of details explanation of this class in later in this post.

3rd parameter of the startDrag method is the Local Object. Mostly, this the object you are going to perform the drag operation. In the above code generator image view itself is passed. This local object information can be captured by the event listener view.


OnDragListener
This is used by the Receiver view to accept the drag. This is heart of all these classes used inside the framework. This the class used by the event listener. Listener view register this class through setOnDragListener method to itself. This interface has one method declared onDrag. Inside this method listener listens for different 6 types of drag events and reacts accordingly. 

All 6 types of Drag events, I have explained using pictures earlier in the post. Here All these events are used inside onDrag method. Some of the key points that must be kept in mind are:
  • DragEvent.ACTION_DRAG_STARTED must return true to get any further Drag event
  • event.getClipData() can only be called if event is DragEvent.ACTION_DROP
  • event.getResult() can only be called if event is DragEvent.ACTION_DRAG_ENDED. This result is true id Drop was successful otherwise false.
class MyDragListener implements View.OnDragListener {

private boolean iGot;
@SuppressWarnings("deprecation")
public boolean onDrag(View v, DragEvent event) {

int dragAction = event.getAction();

switch (dragAction) {

case DragEvent.ACTION_DRAG_STARTED:
v.setBackgroundColor(Color.GREEN);
return true;
case DragEvent.ACTION_DRAG_ENTERED:
v.setBackgroundColor(Color.BLUE);
return true;
case DragEvent.ACTION_DRAG_LOCATION:
Log.i(TAG, "Location: "+event.getX()+":"+event.getY());
return true;
case DragEvent.ACTION_DRAG_EXITED:
v.setBackgroundColor(Color.YELLOW);
return true;
case DragEvent.ACTION_DROP:
ImageView image = (ImageView)event.getLocalState();
v.setBackgroundDrawable(image.getDrawable());
iGot=true;
ClipData clipData= event.getClipData();
Item item = clipData.getItemAt(0);
String str= (String) item.getText();
Log.i(TAG,"Item Text :"+str);
return true;
case DragEvent.ACTION_DRAG_ENDED:
if(!iGot)
v.setBackgroundColor(Color.WHITE);
if(event.getResult())
text.setText("Dropped Successfully");
else
text.setText("Drop not Successful");
return true;
}
return false;
}

}

DragShadowBuilder

It provides a visual shadow for the user to visualize the drag operation. This calss can be used simply by passing a view object inside its constructor.
DragShadowBuilder shadowBuilder = new DragShadowBuilder(v);

v.startDrag(clipData, shadowBuilder, v, 0);
In this case drag shaow is same as the view passed to it. Its size is same as view view size and touch point is centre of the view. 

Touch Point: A point inside the shadow area, generally and by default centre of the shadow. You can change the touch point location for your view shadow.





This is the point of the shadow which is under the user finger. When this point enters a listener view area then only listener can get ACTION_DRAG_ENTERED signal and can accept the drop data.

Size of the shadow is calculated inside the onProvidedMetrics method. Here you can provide your custom dimension of shadow and touch point. Inside the onDrawShadow you can drow your custom drawing on the provided canvas like cicle or rectagle or an image. You can do whatever you can do a canvas.
class MyDragShadow extends View.DragShadowBuilder{
int shadowWidth;
int shadowHeight;
Paint paint;
public MyDragShadow(View v) {
super(v);
paint=new Paint(Paint.ANTI_ALIAS_FLAG);
}
@Override
public void onProvideShadowMetrics(Point shadowSize,
Point shadowTouchPoint) {
shadowWidth=getView().getWidth();
shadowHeight=getView().getHeight();
shadowSize.set(shadowWidth, shadowHeight);
shadowTouchPoint.set(3*shadowWidth/4, 3*shadowHeight/4);
}
@Override
public void onDrawShadow(Canvas canvas) {
paint.setColor(Color.GRAY);
paint.setStyle(Style.FILL_AND_STROKE);
canvas.drawCircle(shadowWidth/2, shadowHeight/2,
shadowHeight/2, paint);
}
}

In the next post we will see a demo application based on this concepts....;)

No comments:

Post a Comment