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
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
* {@link #DRAG_ACTION_COPY}
*/
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
* {@link #DRAG_ACTION_COPY}
*/
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 >= min and < 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>
No comments:
Post a Comment