top of page
Search
Writer's pictureAbhinaw Tripathi

Android Drag View example with source code


Create Class such as 1)DragActivity.java import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; public class DragActivity extends Activity implements View.OnLongClickListener, View.OnClickListener, View.OnTouchListener { private DragController mDragController; // Object that sends out drag-drop events while a view is being moved. private DragLayer mDragLayer; // The ViewGroup that supports drag-drop. private boolean mLongClickStartsDrag = true; // If true, it takes a long click to start the drag operation. // Otherwise, any touch event starts a drag. private static final int CHANGE_TOUCH_MODE_MENU_ID = Menu.FIRST; public static final boolean Debugging = false; /** * onCreate - called when the activity is first created. * * Creates a drag controller and sets up three views so click and long click on the views are sent to this activity. * The onLongClick method starts a drag sequence. * */ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDragController = new DragController(this); setContentView(R.layout.main); setupViews (); } /** * Build a menu for the activity. * */ public boolean onCreateOptionsMenu (Menu menu) { super.onCreateOptionsMenu(menu); menu.add (0, CHANGE_TOUCH_MODE_MENU_ID, 0, "Change Touch Mode"); return true; } /** * Handle a click on a view. Tell the user to use a long click (press). * */ public void onClick(View v) { if (mLongClickStartsDrag) { // Tell the user that it takes a long click to start dragging. toast ("Press and hold to drag an image."); } } /** * Handle a long click. * If mLongClick only is true, this will be the only way to start a drag operation. * * @param v View * @return boolean - true indicates that the event was handled */ public boolean onLongClick(View v) { if (mLongClickStartsDrag) { //trace ("onLongClick in view: " + v + " touchMode: " + v.isInTouchMode ()); // Make sure the drag was started by a long press as opposed to a long click. // (Note: I got this from the Workspace object in the Android Launcher code. // I think it is here to ensure that the device is still in touch mode as we start the drag operation.) if (!v.isInTouchMode()) { toast ("isInTouchMode returned false. Try touching the view again."); return false; } return startDrag (v); } // If we get here, return false to indicate that we have not taken care of the event. return false; } /** * Perform an action in response to a menu item being clicked. * */ public boolean onOptionsItemSelected (MenuItem item) { switch (item.getItemId()) { case CHANGE_TOUCH_MODE_MENU_ID: mLongClickStartsDrag = !mLongClickStartsDrag; String message = mLongClickStartsDrag ? "Changed touch mode. Drag now starts on long touch (click)." : "Changed touch mode. Drag now starts on touch (click)."; Toast.makeText (getApplicationContext(), message, Toast.LENGTH_LONG).show (); return true; } return super.onOptionsItemSelected (item); } /** * This is the starting point for a drag operation if mLongClickStartsDrag is false. * It looks for the down event that gets generated when a user touches the screen. * Only that initiates the drag-drop sequence. * */ public boolean onTouch (View v, MotionEvent ev) { // If we are configured to start only on a long click, we are not going to handle any events here. if (mLongClickStartsDrag) return false; boolean handledHere = false; final int action = ev.getAction(); // In the situation where a long click is not needed to initiate a drag, simply start on the down event. if (action == MotionEvent.ACTION_DOWN) { handledHere = startDrag (v); } return handledHere; } /** * Start dragging a view. * */ public boolean startDrag (View v) { // Let the DragController initiate a drag-drop sequence. // I use the dragInfo to pass along the object being dragged. // I'm not sure how the Launcher designers do this. Object dragInfo = v; mDragController.startDrag (v, mDragLayer, dragInfo, DragController.DRAG_ACTION_MOVE); return true; } /** * Finds all the views we need and configure them to send click events to the activity. * */ private void setupViews() { DragController dragController = mDragController; mDragLayer = (DragLayer) findViewById(R.id.drag_layer); mDragLayer.setDragController(dragController); dragController.addDropTarget (mDragLayer); TextView i1 = (TextView) findViewById (R.id.Image1); ImageView i2 = (ImageView) findViewById (R.id.Image2); i1.setOnClickListener(this); i1.setOnLongClickListener(this); i1.setOnTouchListener(this); i2.setOnClickListener(this); i2.setOnLongClickListener(this); i2.setOnTouchListener(this); TextView tv = (TextView) findViewById (R.id.Text1); tv.setOnLongClickListener(this); tv.setOnTouchListener(this); String message = mLongClickStartsDrag ? "Press and hold to start dragging." : "Touch a view to start dragging."; Toast.makeText (getApplicationContext(), message, Toast.LENGTH_LONG).show (); } /** * Show a string on the screen via Toast. * * @param msg String * @return void */ public void toast (String msg) { Toast.makeText (getApplicationContext(), msg, Toast.LENGTH_SHORT).show (); } // end toast /** * Send a message to the debug log and display it using Toast. */ public void trace (String msg) { if (!Debugging) return; Log.d ("DragActivity", msg); toast (msg); } } // end class

2)DragController.java

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.Rect;

import android.os.IBinder;

import android.os.Vibrator;

import android.util.DisplayMetrics;

import android.util.Log;

import android.view.View;

import android.view.KeyEvent;

import android.view.MotionEvent;

import android.view.WindowManager;

import android.view.inputmethod.InputMethodManager;

import java.util.ArrayList;

/**

* This class is used to initiate a drag within a view or across multiple views.

* When a drag starts it creates a special view (a DragView) that moves around the screen

* until the user ends the drag. As feedback to the user, this object causes the device to

* vibrate as the drag begins.

*

*/

public class DragController {

private static final String TAG = "DragController";

/** Indicates the drag is a move. */

public static int DRAG_ACTION_MOVE = 0;

/** Indicates the drag is a copy. */

public static int DRAG_ACTION_COPY = 1;

private static final int VIBRATE_DURATION = 35;

private static final boolean PROFILE_DRAWING_DURING_DRAG = false;

private Context mContext;

private Vibrator mVibrator;

// temporaries to avoid gc thrash

private Rect mRectTemp = new Rect();

private final int[] mCoordinatesTemp = new int[2];

/** Whether or not we're dragging. */

private boolean mDragging;

/** X coordinate of the down event. */

private float mMotionDownX;

/** Y coordinate of the down event. */

private float mMotionDownY;

/** Info about the screen for clamping. */

private DisplayMetrics mDisplayMetrics = new DisplayMetrics();

/** Original view that is being dragged. */

private View mOriginator;

/** X offset from the upper-left corner of the cell to where we touched. */

private float mTouchOffsetX;

/** Y offset from the upper-left corner of the cell to where we touched. */

private float mTouchOffsetY;

/** Where the drag originated */

private DragSource mDragSource;

/** The data associated with the object being dragged */

private Object mDragInfo;

/** The view that moves around while you drag. */

private DragView mDragView;

/** Who can receive drop events */

private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();

private DragListener mListener;

/** The window token used as the parent for the DragView. */

private IBinder mWindowToken;

private View mMoveTarget;

private DropTarget mLastDropTarget;

private InputMethodManager mInputMethodManager;

/**

* Interface to receive notifications when a drag starts or stops

*/

interface DragListener {

/**

* A drag has begun

*

* @param source An object representing where the drag originated

* @param info The data associated with the object that is being dragged

* @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE}

* or {@link DragController#DRAG_ACTION_COPY}

*/

void onDragStart(DragSource source, Object info, int dragAction);

/**

* The drag has eneded

*/

void onDragEnd();

}

/**

* Used to create a new DragLayer from XML.

*

* @param context The application's context.

*/

public DragController(Context context) {

mContext = context;

mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);

}

/**

* Starts a drag.

* It creates a bitmap of the view being dragged. That bitmap is what you see moving.

* The actual view can be repositioned if that is what the onDrop handle chooses to do.

*

* @param v The view that is being dragged

* @param source An object representing where the drag originated

* @param dragInfo The data associated with the object that is being dragged

* @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or

*/

public void startDrag(View v, DragSource source, Object dragInfo, int dragAction) {

// Start dragging, but only if the source has something to drag.

boolean doDrag = source.allowDrag ();

if (!doDrag) return;

mOriginator = v;

Bitmap b = getViewBitmap(v);

if (b == null) {

// out of memory?

return;

}

int[] loc = mCoordinatesTemp;

v.getLocationOnScreen(loc);

int screenX = loc[0];

int screenY = loc[1];

startDrag(b, screenX, screenY, 0, 0, b.getWidth(), b.getHeight(),

source, dragInfo, dragAction);

b.recycle();

if (dragAction == DRAG_ACTION_MOVE) {

v.setVisibility(View.GONE);

}

}

/**

* Starts a drag.

*

* @param b The bitmap to display as the drag image. It will be re-scaled to the

* enlarged size.

* @param screenX The x position on screen of the left-top of the bitmap.

* @param screenY The y position on screen of the left-top of the bitmap.

* @param textureLeft The left edge of the region inside b to use.

* @param textureTop The top edge of the region inside b to use.

* @param textureWidth The width of the region inside b to use.

* @param textureHeight The height of the region inside b to use.

* @param source An object representing where the drag originated

* @param dragInfo The data associated with the object that is being dragged

* @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or

*/

public void startDrag(Bitmap b, int screenX, int screenY,

int textureLeft, int textureTop, int textureWidth, int textureHeight,

DragSource source, Object dragInfo, int dragAction) {

if (PROFILE_DRAWING_DURING_DRAG) {

android.os.Debug.startMethodTracing("Launcher");

}

// Hide soft keyboard, if visible

if (mInputMethodManager == null) {

mInputMethodManager = (InputMethodManager)

mContext.getSystemService(Context.INPUT_METHOD_SERVICE);

}

mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);

if (mListener != null) {

mListener.onDragStart(source, dragInfo, dragAction);

}

int registrationX = ((int)mMotionDownX) - screenX;

int registrationY = ((int)mMotionDownY) - screenY;

mTouchOffsetX = mMotionDownX - screenX;

mTouchOffsetY = mMotionDownY - screenY;

mDragging = true;

mDragSource = source;

mDragInfo = dragInfo;

mVibrator.vibrate(VIBRATE_DURATION);

DragView dragView = mDragView = new DragView(mContext, b, registrationX, registrationY,

textureLeft, textureTop, textureWidth, textureHeight);

dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);

}

/**

* Draw the view into a bitmap.

*/

private Bitmap getViewBitmap(View v) {

v.clearFocus();

v.setPressed(false);

boolean willNotCache = v.willNotCacheDrawing();

v.setWillNotCacheDrawing(false);

// Reset the drawing cache background color to fully transparent

// for the duration of this operation

int color = v.getDrawingCacheBackgroundColor();

v.setDrawingCacheBackgroundColor(0);

if (color != 0) {

v.destroyDrawingCache();

}

v.buildDrawingCache();

Bitmap cacheBitmap = v.getDrawingCache();

if (cacheBitmap == null) {

Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());

return null;

}

Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);

// Restore the view

v.destroyDrawingCache();

v.setWillNotCacheDrawing(willNotCache);

v.setDrawingCacheBackgroundColor(color);

return bitmap;

}

/**

* Call this from a drag source view like this:

*

* <pre>

* @Override

* public boolean dispatchKeyEvent(KeyEvent event) {

* return mDragController.dispatchKeyEvent(this, event)

* || super.dispatchKeyEvent(event);

* </pre>

*/

public boolean dispatchKeyEvent(KeyEvent event) {

return mDragging;

}

/**

* Stop dragging without dropping.

*/

public void cancelDrag() {

endDrag();

}

private void endDrag() {

if (mDragging) {

mDragging = false;

if (mOriginator != null) {

mOriginator.setVisibility(View.VISIBLE);

}

if (mListener != null) {

mListener.onDragEnd();

}

if (mDragView != null) {

mDragView.remove();

mDragView = null;

}

}

}

/**

* Call this from a drag source view.

*/

public boolean onInterceptTouchEvent(MotionEvent ev) {

final int action = ev.getAction();

if (action == MotionEvent.ACTION_DOWN) {

recordScreenSize();

}

final int screenX = clamp((int)ev.getRawX(), 0, mDisplayMetrics.widthPixels);

final int screenY = clamp((int)ev.getRawY(), 0, mDisplayMetrics.heightPixels);

switch (action) {

case MotionEvent.ACTION_MOVE:

break;

case MotionEvent.ACTION_DOWN:

// Remember location of down touch

mMotionDownX = screenX;

mMotionDownY = screenY;

mLastDropTarget = null;

break;

case MotionEvent.ACTION_CANCEL:

case MotionEvent.ACTION_UP:

if (mDragging) {

drop(screenX, screenY);

}

endDrag();

break;

}

return mDragging;

}

/**

* Sets the view that should handle move events.

*/

void setMoveTarget(View view) {

mMoveTarget = view;

}

public boolean dispatchUnhandledMove(View focused, int direction) {

return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction);

}

/**

* Call this from a drag source view.

*/

public boolean onTouchEvent(MotionEvent ev) {

if (!mDragging) {

return false;

}

final int action = ev.getAction();

final int screenX = clamp((int)ev.getRawX(), 0, mDisplayMetrics.widthPixels);

final int screenY = clamp((int)ev.getRawY(), 0, mDisplayMetrics.heightPixels);

switch (action) {

case MotionEvent.ACTION_DOWN:

// Remember where the motion event started

mMotionDownX = screenX;

mMotionDownY = screenY;

break;

case MotionEvent.ACTION_MOVE:

// Update the drag view. Don't use the clamped pos here so the dragging looks

// like it goes off screen a little, intead of bumping up against the edge.

mDragView.move((int)ev.getRawX(), (int)ev.getRawY());

// Drop on someone?

final int[] coordinates = mCoordinatesTemp;

DropTarget dropTarget = findDropTarget(screenX, screenY, coordinates);

if (dropTarget != null) {

if (mLastDropTarget == dropTarget) {

dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1],

(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);

} else {

if (mLastDropTarget != null) {

mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],

(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);

}

dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1],

(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);

}

} else {

if (mLastDropTarget != null) {

mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],

(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);

}

}

mLastDropTarget = dropTarget;

/* The original Launcher activity supports a delete region and scrolling.

It is not needed in this example.

// Scroll, maybe, but not if we're in the delete region.

boolean inDeleteRegion = false;

if (mDeleteRegion != null) {

inDeleteRegion = mDeleteRegion.contains(screenX, screenY);

}

//Log.d(TAG, "inDeleteRegion=" + inDeleteRegion + " screenX=" + screenX

// + " mScrollZone=" + mScrollZone);

if (!inDeleteRegion && screenX < mScrollZone) {

if (mScrollState == SCROLL_OUTSIDE_ZONE) {

mScrollState = SCROLL_WAITING_IN_ZONE;

mScrollRunnable.setDirection(SCROLL_LEFT);

mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);

}

} else if (!inDeleteRegion && screenX > scrollView.getWidth() - mScrollZone) {

if (mScrollState == SCROLL_OUTSIDE_ZONE) {

mScrollState = SCROLL_WAITING_IN_ZONE;

mScrollRunnable.setDirection(SCROLL_RIGHT);

mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);

}

} else {

if (mScrollState == SCROLL_WAITING_IN_ZONE) {

mScrollState = SCROLL_OUTSIDE_ZONE;

mScrollRunnable.setDirection(SCROLL_RIGHT);

mHandler.removeCallbacks(mScrollRunnable);

}

}

*/

break;

case MotionEvent.ACTION_UP:

if (mDragging) {

drop(screenX, screenY);

}

endDrag();

break;

case MotionEvent.ACTION_CANCEL:

cancelDrag();

}

return true;

}

private boolean drop(float x, float y) {

final int[] coordinates = mCoordinatesTemp;

DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);

if (dropTarget != null) {

dropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],

(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);

if (dropTarget.acceptDrop(mDragSource, coordinates[0], coordinates[1],

(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo)) {

dropTarget.onDrop(mDragSource, coordinates[0], coordinates[1],

(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);

mDragSource.onDropCompleted((View) dropTarget, true);

return true;

} else {

mDragSource.onDropCompleted((View) dropTarget, false);

return true;

}

}

return false;

}

private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {

final Rect r = mRectTemp;

final ArrayList<DropTarget> dropTargets = mDropTargets;

final int count = dropTargets.size();

for (int i=count-1; i>=0; i--) {

final DropTarget target = dropTargets.get(i);

target.getHitRect(r);

target.getLocationOnScreen(dropCoordinates);

r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());

if (r.contains(x, y)) {

dropCoordinates[0] = x - dropCoordinates[0];

dropCoordinates[1] = y - dropCoordinates[1];

return target;

}

}

return null;

}

/**

* Get the screen size so we can clamp events to the screen size so even if

* you drag off the edge of the screen, we find something.

*/

private void recordScreenSize() {

((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))

.getDefaultDisplay().getMetrics(mDisplayMetrics);

}

/**

* Clamp val to be &gt;= min and &lt; max.

*/

private static int clamp(int val, int min, int max) {

if (val < min) {

return min;

} else if (val >= max) {

return max - 1;

} else {

return val;

}

}

public void setWindowToken(IBinder token) {

mWindowToken = token;

}

/**

* Sets the drag listner which will be notified when a drag starts or ends.

*/

public void setDragListener(DragListener l) {

mListener = l;

}

/**

* Remove a previously installed drag listener.

*/

public void removeDragListener(DragListener l) {

mListener = null;

}

/**

* Add a DropTarget to the list of potential places to receive drop events.

*/

public void addDropTarget(DropTarget target) {

mDropTargets.add(target);

}

/**

* Don't send drop events to <em>target</em> any more.

*/

public void removeDropTarget(DropTarget target) {

mDropTargets.remove(target);

}

}

3)DragLayer.java

import android.content.Context;

import android.graphics.Rect;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.KeyEvent;

import android.view.View;

import android.widget.Toast;

/**

* A ViewGroup that coordinates dragging across its dscendants.

*

* <p> This class used DragLayer in the Android Launcher activity as a model.

* It is a bit different in several respects:

* (1) It extends MyAbsoluteLayout rather than FrameLayout; (2) it implements DragSource and DropTarget methods

* that were done in a separate Workspace class in the Launcher.

*/

public class DragLayer extends MyAbsoluteLayout

implements DragSource, DropTarget

{

DragController mDragController;

/**

* Used to create a new DragLayer from XML.

*

* @param context The application's context.

* @param attrs The attribtues set containing the Workspace's customization values.

*/

public DragLayer (Context context, AttributeSet attrs) {

super(context, attrs);

}

public void setDragController(DragController controller) {

mDragController = controller;

}

@Override

public boolean dispatchKeyEvent(KeyEvent event) {

return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

return mDragController.onInterceptTouchEvent(ev);

}

@Override

public boolean onTouchEvent(MotionEvent ev) {

return mDragController.onTouchEvent(ev);

}

@Override

public boolean dispatchUnhandledMove(View focused, int direction) {

return mDragController.dispatchUnhandledMove(focused, direction);

}

/**

*/

// DragSource interface methods

/**

* This method is called to determine if the DragSource has something to drag.

*

* @return True if there is something to drag

*/

public boolean allowDrag () {

// In this simple demo, any view that you touch can be dragged.

return true;

}

/**

* setDragController

*

*/

/* setDragController is already defined. See above. */

/**

* onDropCompleted

*

*/

public void onDropCompleted (View target, boolean success)

{

toast ("DragLayer2.onDropCompleted: " + target.getId () + " Check that the view moved.");

}

/**

*/

// DropTarget interface implementation

/**

* Handle an object being dropped on the DropTarget.

* This is the where a dragged view gets repositioned at the end of a drag.

*

* @param source DragSource where the drag started

* @param x X coordinate of the drop location

* @param y Y coordinate of the drop location

* @param xOffset Horizontal offset with the object being dragged where the original

* touch happened

* @param yOffset Vertical offset with the object being dragged where the original

* touch happened

* @param dragView The DragView that's being dragged around on screen.

* @param dragInfo Data associated with the object being dragged

*

*/

public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo)

{

View v = (View) dragInfo;

toast ("DragLayer2.onDrop accepts view: " + v.getId ()

+ "x, y, xO, yO :" + new Integer (x) + ", " + new Integer (y) + ", "

+ new Integer (xOffset) + ", " + new Integer (yOffset));

int w = v.getWidth ();

int h = v.getHeight ();

int left = x - xOffset;

int top = y - yOffset;

DragLayer.LayoutParams lp = new DragLayer.LayoutParams (w, h, left, top);

this.updateViewLayout(v, lp);

}

public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo)

{

}

public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo)

{

}

public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo)

{

}

/**

* Check if a drop action can occur at, or near, the requested location.

* This may be called repeatedly during a drag, so any calls should return

* quickly.

*

* @param source DragSource where the drag started

* @param x X coordinate of the drop location

* @param y Y coordinate of the drop location

* @param xOffset Horizontal offset with the object being dragged where the

* original touch happened

* @param yOffset Vertical offset with the object being dragged where the

* original touch happened

* @param dragView The DragView that's being dragged around on screen.

* @param dragInfo Data associated with the object being dragged

* @return True if the drop will be accepted, false otherwise.

*/

public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo)

{

return true;

}

/**

* Estimate the surface area where this object would land if dropped at the

* given location.

*

* @param source DragSource where the drag started

* @param x X coordinate of the drop location

* @param y Y coordinate of the drop location

* @param xOffset Horizontal offset with the object being dragged where the

* original touch happened

* @param yOffset Vertical offset with the object being dragged where the

* original touch happened

* @param dragView The DragView that's being dragged around on screen.

* @param dragInfo Data associated with the object being dragged

* @param recycle {@link Rect} object to be possibly recycled.

* @return Estimated area that would be occupied if object was dropped at

* the given location. Should return null if no estimate is found,

* or if this target doesn't provide estimations.

*/

public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo, Rect recycle)

{

return null;

}

/**

*/

// More methods

/**

* Show a string on the screen via Toast.

*

* @param msg String

* @return void

*/

public void toast (String msg)

{

if (!DragActivity.Debugging) return;

Toast.makeText (getContext (), msg, Toast.LENGTH_SHORT).show ();

} // end toast

} // end class

4)DragSource.java

import android.view.View;

/**

* Interface defining an object where drag operations originate.

*

*/

public interface DragSource {

/**

* This method is called to determine if the DragSource has something to drag.

*

* @return True if there is something to drag

*/

boolean allowDrag ();

/**

* This method is used to tell the DragSource which drag controller it is working with.

*

* @param dragger DragController

*/

void setDragController(DragController dragger);

/**

* This method is called on the completion of the drag operation so the DragSource knows

* whether it succeeded or failed.

*

* @param target View - the view that accepted the dragged object

* @param success boolean - true means that the object was dropped successfully

*/

void onDropCompleted (View target, boolean success);

}

5)DragView.java

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.Matrix;

import android.graphics.Paint;

import android.graphics.PixelFormat;

import android.os.IBinder;

import android.view.Gravity;

import android.view.View;

import android.view.ViewGroup;

import android.view.WindowManager;

/**

* A DragView is a special view used by a DragController. During a drag operation, what is actually moving

* on the screen is a DragView. A DragView is constructed using a bitmap of the view the user really

* wants to move.

*

*/

public class DragView extends View

{

// Number of pixels to add to the dragged item for scaling. Should be even for pixel alignment.

private static final int DRAG_SCALE = 0; // In Launcher, value is 40

private Bitmap mBitmap;

private Paint mPaint;

private int mRegistrationX;

private int mRegistrationY;

private float mScale;

private float mAnimationScale = 1.0f;

private WindowManager.LayoutParams mLayoutParams;

private WindowManager mWindowManager;

/**

* Construct the drag view.

* <p>

* The registration point is the point inside our view that the touch events should

* be centered upon.

*

* @param context A context

* @param bitmap The view that we're dragging around. We scale it up when we draw it.

* @param registrationX The x coordinate of the registration point.

* @param registrationY The y coordinate of the registration point.

*/

public DragView(Context context, Bitmap bitmap, int registrationX, int registrationY,

int left, int top, int width, int height) {

super(context);

// mWindowManager = WindowManagerImpl.getDefault();

mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

Matrix scale = new Matrix();

float scaleFactor = width;

scaleFactor = mScale = (scaleFactor + DRAG_SCALE) / scaleFactor;

scale.setScale(scaleFactor, scaleFactor);

mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);

// The point in our scaled bitmap that the touch events are located

mRegistrationX = registrationX + (DRAG_SCALE / 2);

mRegistrationY = registrationY + (DRAG_SCALE / 2);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());

}

@Override

protected void onDraw(Canvas canvas) {

if (true) {

// for debugging

Paint p = new Paint();

p.setStyle(Paint.Style.FILL);

p.setColor(0x88dd0011);

canvas.drawRect(0, 0, getWidth(), getHeight(), p);

}

float scale = mAnimationScale;

if (scale < 0.999f) { // allow for some float error

float width = mBitmap.getWidth();

float offset = (width-(width*scale))/2;

canvas.translate(offset, offset);

canvas.scale(scale, scale);

}

canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);

}

@Override

protected void onDetachedFromWindow() {

super.onDetachedFromWindow();

mBitmap.recycle();

}

public void setPaint(Paint paint) {

mPaint = paint;

invalidate();

}

/**

* Create a window containing this view and show it.

*

* @param windowToken obtained from v.getWindowToken() from one of your views

* @param touchX the x coordinate the user touched in screen coordinates

* @param touchY the y coordinate the user touched in screen coordinates

*/

public void show(IBinder windowToken, int touchX, int touchY) {

WindowManager.LayoutParams lp;

int pixelFormat;

pixelFormat = PixelFormat.TRANSLUCENT;

lp = new WindowManager.LayoutParams(

ViewGroup.LayoutParams.WRAP_CONTENT,

ViewGroup.LayoutParams.WRAP_CONTENT,

touchX-mRegistrationX, touchY-mRegistrationY,

WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,

WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN

| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS

/*| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM*/,

pixelFormat);

// lp.token = mStatusBarView.getWindowToken();

lp.gravity = Gravity.LEFT | Gravity.TOP;

lp.token = windowToken;

lp.setTitle("DragView");

mLayoutParams = lp;

mWindowManager.addView(this, lp);

}

/**

* Move the window containing this view.

*

* @param touchX the x coordinate the user touched in screen coordinates

* @param touchY the y coordinate the user touched in screen coordinates

*/

void move(int touchX, int touchY) {

// This is what was done in the Launcher code.

WindowManager.LayoutParams lp = mLayoutParams;

lp.x = touchX - mRegistrationX;

lp.y = touchY - mRegistrationY;

mWindowManager.updateViewLayout(this, lp);

}

void remove() {

mWindowManager.removeView(this);

}

}

6)DropTarget.java

import android.graphics.Rect;

/**

* Interface defining an object that can receive a view at the end of a drag operation.

*

*/

public interface DropTarget {

/**

* Handle an object being dropped on the DropTarget

*

* @param source DragSource where the drag started

* @param x X coordinate of the drop location

* @param y Y coordinate of the drop location

* @param xOffset Horizontal offset with the object being dragged where the original

* touch happened

* @param yOffset Vertical offset with the object being dragged where the original

* touch happened

* @param dragView The DragView that's being dragged around on screen.

* @param dragInfo Data associated with the object being dragged

*

*/

void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo);

void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo);

void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo);

void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo);

/**

* Check if a drop action can occur at, or near, the requested location.

* This may be called repeatedly during a drag, so any calls should return

* quickly.

*

* @param source DragSource where the drag started

* @param x X coordinate of the drop location

* @param y Y coordinate of the drop location

* @param xOffset Horizontal offset with the object being dragged where the

* original touch happened

* @param yOffset Vertical offset with the object being dragged where the

* original touch happened

* @param dragView The DragView that's being dragged around on screen.

* @param dragInfo Data associated with the object being dragged

* @return True if the drop will be accepted, false otherwise.

*/

boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo);

/**

* Estimate the surface area where this object would land if dropped at the

* given location.

*

* @param source DragSource where the drag started

* @param x X coordinate of the drop location

* @param y Y coordinate of the drop location

* @param xOffset Horizontal offset with the object being dragged where the

* original touch happened

* @param yOffset Vertical offset with the object being dragged where the

* original touch happened

* @param dragView The DragView that's being dragged around on screen.

* @param dragInfo Data associated with the object being dragged

* @param recycle {@link Rect} object to be possibly recycled.

* @return Estimated area that would be occupied if object was dropped at

* the given location. Should return null if no estimate is found,

* or if this target doesn't provide estimations.

*/

Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset,

DragView dragView, Object dragInfo, Rect recycle);

// These methods are implemented in Views

void getHitRect(Rect outRect);

void getLocationOnScreen(int[] loc);

int getLeft();

int getTop();

}

7)MyAbsoluteLayout.java

import android.content.Context;

import android.content.res.TypedArray;

import android.util.AttributeSet;

import android.view.View;

import android.view.ViewGroup;

import android.widget.*;

import android.widget.RemoteViews.RemoteView;

/**

* A layout that lets you specify exact locations (x/y coordinates) of its

* children. Absolute layouts are less flexible and harder to maintain than

* other types of layouts without absolute positioning.

*

* <p><strong>XML attributes</strong></p> <p> See {@link

* android.R.styleable#ViewGroup ViewGroup Attributes}, {@link

* android.R.styleable#View View Attributes}</p>

*

* <p>Note: This class is a clone of AbsoluteLayout, which is now deprecated.

*/

@RemoteView

public class MyAbsoluteLayout extends ViewGroup {

public MyAbsoluteLayout(Context context) {

super(context);

}

public MyAbsoluteLayout(Context context, AttributeSet attrs) {

super(context, attrs);

}

public MyAbsoluteLayout(Context context, AttributeSet attrs,

int defStyle) {

super(context, attrs, defStyle);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int count = getChildCount();

int maxHeight = 0;

int maxWidth = 0;

// Find out how big everyone wants to be

measureChildren(widthMeasureSpec, heightMeasureSpec);

// Find rightmost and bottom-most child

for (int i = 0; i < count; i++) {

View child = getChildAt(i);

if (child.getVisibility() != GONE) {

int childRight;

int childBottom;

MyAbsoluteLayout.LayoutParams lp

= (MyAbsoluteLayout.LayoutParams) child.getLayoutParams();

childRight = lp.x + child.getMeasuredWidth();

childBottom = lp.y + child.getMeasuredHeight();

maxWidth = Math.max(maxWidth, childRight);

maxHeight = Math.max(maxHeight, childBottom);

}

}

// Account for padding too

maxWidth += getPaddingLeft () + getPaddingRight ();

maxHeight += getPaddingTop () + getPaddingBottom ();

/* original

maxWidth += mPaddingLeft + mPaddingRight;

maxHeight += mPaddingTop + mPaddingBottom;

*/

// Check against minimum height and width

maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());

maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),

resolveSize(maxHeight, heightMeasureSpec));

}

/**

* Returns a set of layout parameters with a width of

* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},

* a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}

* and with the coordinates (0, 0).

*/

@Override

protected ViewGroup.LayoutParams generateDefaultLayoutParams() {

return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0);

}

@Override

protected void onLayout(boolean changed, int l, int t,

int r, int b) {

int count = getChildCount();

int paddingL = getPaddingLeft ();

int paddingT = getPaddingTop ();

for (int i = 0; i < count; i++) {

View child = getChildAt(i);

if (child.getVisibility() != GONE) {

MyAbsoluteLayout.LayoutParams lp =

(MyAbsoluteLayout.LayoutParams) child.getLayoutParams();

int childLeft = paddingL + lp.x;

int childTop = paddingT + lp.y;

/*

int childLeft = mPaddingLeft + lp.x;

int childTop = mPaddingTop + lp.y;

*/

child.layout(childLeft, childTop,

childLeft + child.getMeasuredWidth(),

childTop + child.getMeasuredHeight());

}

}

}

@Override

public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {

return new MyAbsoluteLayout.LayoutParams(getContext(), attrs);

}

// Override to allow type-checking of LayoutParams.

@Override

protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {

return p instanceof MyAbsoluteLayout.LayoutParams;

}

@Override

protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {

return new LayoutParams(p);

}

/**

* Per-child layout information associated with MyAbsoluteLayout.

* See

* {@link android.R.styleable#MyAbsoluteLayout_Layout Absolute Layout Attributes}

* for a list of all child view attributes that this class supports.

*/

public static class LayoutParams extends ViewGroup.LayoutParams {

/**

* The horizontal, or X, location of the child within the view group.

*/

public int x;

/**

* The vertical, or Y, location of the child within the view group.

*/

public int y;

/**

* Creates a new set of layout parameters with the specified width,

* height and location.

*

* @param width the width, either {@link #MATCH_PARENT},

{@link #WRAP_CONTENT} or a fixed size in pixels

* @param height the height, either {@link #MATCH_PARENT},

{@link #WRAP_CONTENT} or a fixed size in pixels

* @param x the X location of the child

* @param y the Y location of the child

*/

public LayoutParams(int width, int height, int x, int y) {

super(width, height);

this.x = x;

this.y = y;

}

/**

* Creates a new set of layout parameters. The values are extracted from

* the supplied attributes set and context. The XML attributes mapped

* to this set of layout parameters are:

*

* <ul>

* <li><code>layout_x</code>: the X location of the child</li>

* <li><code>layout_y</code>: the Y location of the child</li>

* <li>All the XML attributes from

* {@link android.view.ViewGroup.LayoutParams}</li>

* </ul>

*

* @param c the application environment

* @param attrs the set of attributes from which to extract the layout

* parameters values

*/

public LayoutParams(Context c, AttributeSet attrs) {

super(c, attrs);

/* FIX THIS eventually. Without this, I don't think you can put x and y in layout xml files.

TypedArray a = c.obtainStyledAttributes(attrs,

com.android.internal.R.styleable.AbsoluteLayout_Layout);

x = a.getDimensionPixelOffset(

com.android.internal.R.styleable.AbsoluteLayout_Layout_layout_x, 0);

y = a.getDimensionPixelOffset(

com.android.internal.R.styleable.AbsoluteLayout_Layout_layout_y, 0);

a.recycle();

*/

}

/**

* {@inheritDoc}

*/

public LayoutParams(ViewGroup.LayoutParams source) {

super(source);

}

public String debug(String output) {

return output + "Absolute.LayoutParams={width="

+ sizeToString(width) + ", height=" + sizeToString(height)

+ " x=" + x + " y=" + y + "}";

}

/**

* Converts the specified size to a readable String.

*

* @param size the size to convert

* @return a String instance representing the supplied size

*

* @hide

*/

protected static String sizeToString(int size) {

if (size == WRAP_CONTENT) {

return "wrap-content";

}

if (size == MATCH_PARENT) {

return "match-parent";

}

return String.valueOf(size);

}

} // end class

} // end class

8)main.xml

<?xml version="1.0" encoding="utf-8"?>

<com.blahti.example.drag.DragLayer

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"

android:id="@+id/drag_layer"

android:layout_width="match_parent"

android:layout_height="match_parent">

<TextView

android:id="@+id/Text1"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

/>

<TextView

android:id="@+id/Image1"

android:text="helllo"

android:textColor="@android:color/white"

android:layout_weight="50"

android:adjustViewBounds="true"

android:layout_width="80dp"

android:layout_height="100dp" />

<ImageView

android:id="@+id/Image2"

android:layout_weight="50"

android:adjustViewBounds="true"

android:layout_width="50dp"

android:layout_height="80dp" />

</com.blahti.example.drag.DragLayer>

14 views0 comments

Recent Posts

See All
bottom of page