Android 2.2 Click and Drag Image Centered Under Touch - android

I am trying to drag and drop a crosshair onto a MapView. I have an ImageView button on the screen. When I touch it, the button disappears and a new ImageView appears. The new ImageView is supposed to sit centered under my finger until I release it somewhere, but for some reason the offset is incorrect. The crosshair appears down-right of where I am touching.
The image does move proportionately so I believe the problem is with the offset_x and offset_y which I define in the ACTION_DOWN section. Then, in ACTION_UP I need to createMarker(x,y) on the correct coordinates under my finger, but that is offset incorrectly as well.
I have tried various ways to make it centered, and some are better than others. I have so far been unable to make it work without using magic numbers.
As it is, I am using the click location and subtracting half the picture size. This makes sense to me. It's closer to correct if I subtract the whole picture size. I have tried various examples from the web, all of them suffer from inaccuracies with the View location.
Can you give me some advice?
crosshair = (ImageView)findViewById(R.id.crosshair);
frameLayout = (FrameLayout)findViewById(R.id.mapframe);
params = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
crosshairImage = BitmapFactory.decodeResource(getResources(), R.drawable.crosshair);
crosshair.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
dragStatus = START_DRAGGING;
// hide the button and grab a mobile crosshair
v.setVisibility(View.INVISIBLE);
image = new ImageView(getBaseContext());
image.setImageBitmap(crosshairImage);
// set the image offsets to center the crosshair under my touch
offset_x = (int)event.getRawX() - (int)(crosshairImage.getWidth()/2) ;
offset_y = (int)event.getRawY() - (int)(crosshairImage.getHeight()/2) ;
// set the image location on the screen using padding
image.setPadding(offset_x, offset_y, 0, 0);
// add it to the screen so it shows up
frameLayout.addView(image, params);
frameLayout.invalidate();
if(LOG_V) Log.v(TAG, "Pressed Crosshair");
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if(dragStatus == START_DRAGGING) {
dragStatus = STOP_DRAGGING;
// place a marker on this spot
makeMarker((int)event.getX() + offset_x, (int)event.getY() + offset_y);
// make the button visible again
v.setVisibility(View.VISIBLE);
// remove the mobile crosshair
frameLayout.removeView(image);
if(LOG_V) Log.v(TAG, "Dropped Crosshair");
return true;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (dragStatus == START_DRAGGING) {
// compute how far it has moved from 'start' offset
int x = (int)event.getX() + offset_x;
int y = (int)event.getY() + offset_y;
// check that it's in bounds
int w = getWindowManager().getDefaultDisplay().getWidth();
int h = getWindowManager().getDefaultDisplay().getHeight();
if(x > w)
x = w;
if(y > h)
y = h;
// set the padding to change the image loc
image.setPadding(x, y, 0 , 0);
// draw it
image.invalidate();
frameLayout.requestLayout();
if(LOG_V) Log.v(TAG, "(" + offset_x + ", " + offset_y + ")");
return true;
}
}
return false;
}
});

You have written this code within touch_Down.
offset_x = (int)event.getRawX() - (int)(crosshairImage.getWidth()/2) ;
offset_y = (int)event.getRawY() - (int)(crosshairImage.getHeight()/2) ;
// set the image location on the screen using padding
image.setPadding(offset_x, offset_y, 0, 0);
Try writing it outside the if conditions, just to make sure, it is applied to all the touch events.

You can use new api available in drag and drop
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(null, shadowBuilder, view, 0);
view.setVisibility(View.INVISIBLE);
return true;
} else {
return false;
}
}

Related

ListView onTouchListener is omitting fling

I've wrote this onTouchListener to hide/show actionBar on listview scroll, similar to current functionality in Instagram app. Everything works fine except listView will not fling. I know I've blocked that action somehow, but I'm still a novice and cant figure it out. Can anyone help me understand what's going on here?
list.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int newTextPosition = (int) (text.getY() * -1);
int move;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = event.getY();
maxPath = text.getHeight();
break;
case MotionEvent.ACTION_UP:
v.performClick();
break;
case MotionEvent.ACTION_MOVE:
newY = event.getY();
move = (int) (newY - startY);
// Going UP
if(newY < startY){
if((text.getY() * -1) == text.getHeight()){
startY = event.getY();
return false;
}
int maxPath = (int) (text.getHeight() + text.getY());
if((move * -1) > maxPath){
move = maxPath * -1;
}
text.setY(text.getY() + move);
list.setY(list.getY() + move);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) list.getLayoutParams();
params.height = list.getHeight() + (move * -1);
list.setLayoutParams(params);
break;
}
// Going DOWN
if(newY > startY){
if((text.getY() * -1) == 0){
startY = event.getY();
return false;
}
if(text.getY() >= 0){
move = 0;
}
if(move > newTextPosition)
move = newTextPosition;
text.setY(text.getY() + move);
list.setY(list.getY() + move);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) list.getLayoutParams();
params.height = list.getHeight() + (move * -1);
list.setLayoutParams(params);
break;
}
}
return true;
}
});
EDIT: From some reason returning false at the end did the job. Any explanation is welcome
Captain here with an explanation!
When you return true from overriding onTouchEvent() on Views or from the OnTouchListeners onTouch() method, you tell android that this touch event has been 'consumed'. This way the touch event won't be passed back to the underlying views and will stop there. This is good when you need to override what happens on a touch event, but if you need the default action to happen you must return false, this way you tell android that the event wasn't 'consumed' and it must notify every other listener that a touch event has happened.
So by returning true, you had overridden the default behavior, which was the swipe and so it wasn't called on any touch event, because you 'consumed' all touch events.

Android GridView Custom Drag and Drop

I have a gridview that I need to implement drag and drop feature. I've been trying to find a possible solution or existing library that would fit my problem but still no luck.
Here's a before and after illustration of the drag and drop that I need to implement:
The red tiles are items wherein they cannot be dragged nor dropped on. the blue ones are draggable and can be dropped on any tile on any row, just not on the red tiles. The white tiles are just placeholders which are placed to have the red tiles on the first column.
Now, when tile A is dragged on the 3rd row, as you can see, they go side by side, not swapped, even if put on top of tile C. The number of white tiles is depending on the number of blue tiles per row, an arraylist is assigned on each row, so it'll just follow. My real problem is that all examples on gridview drag and drop is that tiles swaps or the whole grid follows the flow of items.
My plan to implement this:
When long pressed on a tile to be dragged, it will show a tile that looks like that tile, only larger and lesser opacity.
When dropped on a certain position, will compute for the row.
Adjust arraylists and notifydatasetchanged.
Here's breaking down the problem to slightly smaller problems:
How can I make a larger tile of the long pressed tile?
Is it possible to get the position where the enlarged tile is dropped?
1.How can I make a larger tile of the long pressed tile? yes,you can get the tile's view and create a new bitmap,the add the bitmap to Windows.Like this : it's a class extends GridView.
public boolean setOnItemLongClickListener(final MotionEvent ev)
{
this.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener()
{
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3)
{
// onInterceptTouchEvent(ev);
// TODO Auto-generated method stub
L.l("============on Long Click=========");
L.l("============X:" + ev.getX() + " Y:" + ev.getY());
int x = (int) ev.getX();
int y = (int) ev.getY();
dragPosition = dropPosition = pointToPosition(x, y);
System.out.println(dragPosition);
if (dragPosition == AdapterView.INVALID_POSITION)
{
}
ViewGroup itemView = (ViewGroup) getChildAt(dragPosition
- getFirstVisiblePosition());
dragPointX = x - itemView.getLeft();
dragPointY = y - itemView.getTop();
dragOffsetX = (int) (ev.getRawX() - x);
dragOffsetY = (int) (ev.getRawY() - y);
itemHeight=itemView.getHeight();
L.l("========================y:" + y + " getRawY:"
+ ev.getRawY());
itemView.destroyDrawingCache();
itemView.setDrawingCacheEnabled(true);
Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());
startDrag(bm, x, y);
return false;
};
});
return super.onInterceptTouchEvent(ev);
}
private void startDrag(Bitmap bm, int x, int y)
{
windowParams = new WindowManager.LayoutParams();
windowParams.gravity = Gravity.TOP | Gravity.LEFT;// 这个必须加
windowParams.x = x - dragPointX + dragOffsetX;
windowParams.y = y - dragPointY + dragOffsetY;
windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
windowParams.format = PixelFormat.TRANSLUCENT;
windowParams.windowAnimations = 0;
ImageView iv = new ImageView(getContext());
iv.setImageBitmap(bm);
windowManager = (WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE);// "window"
windowManager.addView(iv, windowParams);
dragImageView = iv;
}
#Override
public boolean onTouchEvent(MotionEvent ev)
{
if (dragImageView != null
&& dragPosition != AdapterView.INVALID_POSITION)
{
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction())
{
case MotionEvent.ACTION_MOVE:
onDrag(x, y);
break;
case MotionEvent.ACTION_UP:
stopDrag();
onDrop(x, y);
break;
}
}
return super.onTouchEvent(ev);
}
private void onDrag(int x, int y)
{
if (dragImageView != null)
{
windowParams.alpha = 0.6f;
windowParams.x = x - dragPointX + dragOffsetX;
windowParams.y = y - dragPointY + dragOffsetY;
// L.l("=================windowParams.y=====000========"+windowParams.y);
windowManager.updateViewLayout(dragImageView, windowParams);
}
int tempScrollX = x - dragPointX + dragOffsetX;
int tempScrollY = y - dragPointY + dragOffsetY;
if (tempScrollY +itemHeight> 600)
{
this.scrollTo(0, tempScrollY);
}
else
if (pointToPosition(x, y) > 2)
{
this.scrollTo(0, tempScrollY - 300);
}
}
2.Is it possible to get the position where the enlarged tile is dropped?
If your class is extends GridView or AbsListView,this API `pointToPosition(x, y) will return the position of the whole view.
When you drop tile A,then you calculate where tile A is now,if above tile C,then start a Animation(C move to side and A take place position of C) and update the Adapter end of the Animation.
I ended up editting http://code.google.com/p/android-gridview-drag-and-drop/ to fit my problem.

GraphView how to Display Toast near the user click point

Hello i would like to ask, how can i Display Toast near the user click point Like on image below using GraphView library:
http://android-graphview.org/
Thanks for any advice
EDIT:
I tried it using
seriesSin = new GraphViewSeries("Sinus curve", new GraphViewSeries.GraphViewSeriesStyle(Color.rgb(200, 50, 00), 3), data);
// SET ON TOUCH
graphView.setOnTouchListener(new View.OnTouchListener(){
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
int size = seriesSin.size();
float screenX = event.getX();
float screenY = event.getY();
float width_x = v.getWidth();
float viewX = screenX - v.getLeft();
float viewY = screenY - v.getTop();
float percent_x = (viewX/width_x);
int pos = (int) (size*percent_x);
System.out.println("X: " + viewX + " Y: " + viewY +" Percent = " +percent_x);
System.out.println("YVal = " +seriesSin.getY(pos));
return true;
}
return false;
}
});
But I cannot to get seriesSin.size and seriesSin.getY(pos)
You can't decide where to display the Toast. However, you could set up a custom view and show it using the coordinates (x and y) you get when detecting the touch. When you show the view, simply use those values to set up the layout params of the view, and voila.
Version 4.0.0 added an onTap() listener, that replicates your onTouch method above, it doesn't solve your Toast positioning issue, but could save you a few lines of code.

Dragging custom view in scroll area

So I have a custom view widget I am allowing a user to drag around by their finger. They are dragging it in a custom scrolling view based on a FrameLayout (code here: http://blog.gorges.us/2010/06/android-two-dimensional-scrollview/) that contains a RelativeLayout, so that they can place it where they want and have plenty of space that is not visible on screen to place the item as well.
What I would like to happen is if the user is moving the widget near the edge of the visible scroll area is to scroll the area in the appropriate direction until they either let go (ACTION_UP, in which case no more events would take place anyway) or have moved away from the edge. The code right now has two problems:
1) My phone (T-Mobile G2) continues to trigger ACTION_MOVE even when my finger is still where as my Nexus 7 tablet does not continue to trigger until it has moved. I need to reliably continue to trigger this event until ACTION_UP.
2) The area scrolls but only as I move my finger towards the edge of the screen. In otherwords, I'd like for a finger that is staying still to continue to scroll and move the object when it is near the edge, but this is not happening.
What am I missing? Here is the onTouchEvent that handles the moving of the widget
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE
&& this.getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
// mScrollParent gets set in a setter and points to the TwoDScrollView
// mOffsetX/Y is based on where the user touches versus the position of the custom view
// mWidth/mHeight gets set once in the GestureDetector based on the custom view's getWidth()/getHeight()
// mTitleBarHeight gets set once in the GestureDetctor onDown event
// lp is a ViewGroup.LayoutParams type
if (!mGestureDetector.onTouchEvent(event)) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
int finalX = (int) (event.getRawX() - mOffsetX);
int finalY = (int) (event.getRawY() - mOffsetY - (mTitleBarHeight > 0 ? mTitleBarHeight * 2
: 0));
if (finalX < 0 || finalY < 0)
return false;
int scrollLeft = (int) Math.ceil(mScrollParent.getScrollX() * 1.05);
int scrollTop = (int) Math.ceil(mScrollParent.getScrollY() * 1.05);
int scrollRight = (int) Math.floor((scrollLeft + mScrollParent.getRight()) * 0.95);
int scrollBottom = (int) Math.floor((scrollTop + mScrollParent.getBottom()) * 0.95);
int scrollX = 0;
int scrollY = 0;
if (scrollLeft > finalX)
scrollX = finalX - scrollLeft;
if (scrollRight < (finalX + mWidth))
scrollX = (finalX + mWidth)- scrollRight;
if (scrollTop > finalY)
scrollY = finalY - scrollTop;
if (scrollBottom < (finalY + mHeight))
scrollY = (finalY + mHeight) - scrollBottom;
if (scrollX != 0 || scrollY != 0)
mScrollParent.scrollBy(scrollX * 4, scrollY * 4);
lp.setMargins(finalX + scrollX, finalY + scrollY, 0, 0);
setLayoutParams(lp);
return true;
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
return true;
} else {
return super.onTouchEvent(event);
}
} else {
return true;
}
}

OnTouch bitmap overlayed

I'm developing a game where different bitmap run on the screen (a SurfaceView). Since they have different speed, it happen that sometimes two characters are superimposed, one behind the other. I've used the OnTouch function to catch the moment when the player touch the character. However, when two characters are superimposed, I always "touch" the one that is behind, I never touch the other one in front of him. Why? How I can change this behaviour? Since I'm touching the one in front, it's is really annoying that I always touch the other behind.
I hope the problem is clear.
Thanks!
Edit: This is my OnTouchEvent function:
#Override
public boolean onTouchEvent(MotionEvent event){
synchronized (getHolder()) {
boolean chibi_toccato = false;
if (!blocco_tocco){
// Here I have the structure (a Vector) containing the chibis to be shown
for (int i = (mChibiVector.size() - 1); i >= 0; i--) {
Chibi chibi = mChibiVector.elementAt(i);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (!chibi_toccato){
// touched is a flag that tell if a chibi were touched before
if (chibi.touched) {
// if I touch on the boat bitmap (somewhere on the screen) the chibi will be safe
int navexend = posiz_nave_x + thread.mNave.getWidth();
int naveyend = posiz_nave_y + thread.mNave.getHeight();
if ((int) event.getX() >= posiz_nave_x &&
(int) event.getX() <= navexend &&
(int) event.getY() >= posiz_nave_y &&
(int) event.getY() <= naveyend){
chibi.chibisalvo = true;
thread.calcola_vel_x(chibi);
thread.calcola_vel_y(chibi);
} else {
chibi.touched = false;
int temp_chibi_y = chibi.getCoordinate().getY();
chibi.getCoordinate().setY(
temp_chibi_y +
chibi.getBitmapDimensions()[1]);
}
chibi_toccato = true;
} else {
// Here I check if a chibi is touched:
chibi = thread.checkTouched(chibi, (int) event.getX(),
(int) event.getY());
if (chibi.touched){
// If the chibi is touched, I save the X-Y info:
chibi.getCoordinate().setTouchedX((int) event.getX());
chibi.getCoordinate().setTouchedY((int) event.getY());
int temp_chibi_y = chibi.getCoordinate().getY();
chibi.getCoordinate().setY(
temp_chibi_y -
chibi.getBitmapDimensions()[1]);
chibi_toccato = true;
}
}
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (chibi.touched){
chibi_toccato = false;
int navexend = posiz_nave_x + thread.mNave.getWidth();
int naveyend = posiz_nave_y + thread.mNave.getHeight();
if ((int) event.getX() >= posiz_nave_x &&
(int) event.getX() <= navexend &&
(int) event.getY() >= posiz_nave_y &&
(int) event.getY() <= naveyend){
chibi.chibisalvo = true;
thread.calcola_vel_x(chibi);
thread.calcola_vel_y(chibi);
}
}
}
}
}
}
return true;
}
Well, after a while, finally I got the issue. The problem were in the for cycle:
for (int i = (mChibiVector.size() - 1); i >= 0; i--)
Since I started to pick the frames from the end of the vector, the first touch were always on the image behind the others. I'm still wondering why, but finally I changed the for cycle starting from the first vector item to the end and it worked.
Hope it could be useful for others.

Categories

Resources