Android move view to a touched position - android

I'm trying to develop an android application similar to "on color measurement" or "color grab" which are already on google play.
I have problem with getting the exact position of a view (which acts like focus square) and move it to the position where the user touches the screen. I have done a lot of searches but all of them ended in onTouchEvent() which I used it but it does not work properly or maybe I have done something wrong.
Actually the view moves but it won't be placed exactly where the user touch, it will go below the touched area in y axis with a distance.
here is my code where the mFocusRectangle is the custom view which I want to move to the touched position:
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (event.getPointerCount() > 1) {
// handle multi-touch events
} else {
// handle single touch events
if(action==MotionEvent.ACTION_DOWN){
}
if (action == MotionEvent.ACTION_UP) {
int pointerId = event.getPointerId(0);
int pointerIndex = event.findPointerIndex(pointerId);
// Get the pointer's current position
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
mFocusRectangle.showStart();
mFocusRectangle.setX(x);
mFocusRectangle.setY(y);
}
}
return true;
}
I have also tried MotionEvent.ACTION_DOWN but the result is the same.
Thanks in advance.

try this
float x = event.getX(pointerIndex) - (mFocusRectangle.getWidth() / 2);
float y = event.getY(pointerIndex) - (mFocusRectangle.getHeight() / 2);
credits:
Manitoba - Android move view on touch event

Related

Increase touch slop in Android button

We have an Android app that is meant to be mounted on the dash of a vehicle that is operating in rough terrain, so everything is really shaky. We've found that making a single tap on the screen in this kind of situation is difficult, as the taps are often interpreted as small drags.
What I need is for touch events that have a little wiggle before the finger comes up to be interpreted as clicks, not drags. I've been doing some reading into the way the 'touch slop' works in Android and I can see that they already account for this. All I really need to understand is how to increase the 'touch slop' for a subclass of an Android button widget.
Is this possible in just a couple of lines of code? Or do I need to do my own implementations of onInterceptTouchEvent and 'onTouchEvent`? If the latter, can anyone give me some direction on how this would work?
Here is what I did, hope that help you guys.
private Rect mBtnRect;
yourView.setOnTouchListener(new OnTouchListener() {
private boolean isCancelled = false;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isCancelled = false;
parent.requestDisallowInterceptTouchEvent(true); // prevent parent and its ancestors to intercept touch events
createClickArea(v);
// your logic touch down
return true;
case MotionEvent.ACTION_UP:
if(!isCancelled) {
// Click logic
}
// release mBtnRect when cancel or up event
mBtnRect = null;
return true;
case MotionEvent.ACTION_CANCEL:
isCancelled = true;
releaseTouch(parent, v);
return true;
case MotionEvent.ACTION_MOVE:
if(!isBelongTouchArea(event.getRawX(), event.getRawY())) {
isCancelled = true;
releaseTouch(parent, v);
}
return true;
default:
break;
}
}
// Create the area from the view that user is touching
private final void createClickArea(View v) {
// for increase rect area of button, pixel in used.
final int delta = (int) mContext.getResources().getDimension(R.dimen.extension_area);
final int[] location = new int[2];
// Get the location of button call on screen
v.getLocationOnScreen(location);
// Create the rect area with an extension defined distance.
mBtnRect = new Rect(v.getLeft() - delta, location[1] + v.getTop() - delta, v.getRight(), location[1] + v.getBottom() + delta);
}
//Check the area that contains the moved position or not.
private final boolean isBelongTouchArea(float rawX, float rawY) {
if(mBtnRect != null && mBtnRect.contains((int)rawX, (int)rawY)) {
return true;
}
return false;
}
private void releaseTouch(final ListView parent, View v) {
parent.requestDisallowInterceptTouchEvent(false);
mBtnRect = null;
// your logic
}
For our simple use-case with a android.opengl.GLSurfaceView, we solved the same problem you have by saying, "if the distance moved in the gesture is less than some_threshold, interpret it as a click". We used standard Euclidean distance between two 2D points = sqrt((deltaX)^2 + (deltaY)^2)), where deltaX and deltaY are the X and Y components of the distance moved in the user's motion gesture.
More precisely, let (x1,y1) be coordinates where user's finger gesture 'started' and let (x2,y2) be coordinates where the gesture 'ended'.
Then, deltaX = x2 - x1 (or it can also be x1 - x2 but sign doesn't matter for the distance 'cz we square the value) and similarly for deltaY.
From these deltas, we calculate the Euclidean distance and if it's smaller than a threshold, the user probably intended it to be a click but because of the device's touch slop, it got classified as a move gesture instead of click.
Implementation-wise, yes we overrode android.view.View#onTouchEvent(MotionEvent) and
recorded (x1,y1) when masked action was android.view.MotionEvent#ACTION_POINTER_DOWN or android.view.MotionEvent#ACTION_DOWN
and when masked action was android.view.MotionEvent#ACTION_MOVE, (x2,y2) are readily available from the event's getX and getY methods (see corresponding documentation)

How do you make an onTouchListener keep going as long as the view is being pressed?

I am using a small square RelativeLayout as a game pad for movement in a neighboring GLSurfaceView.
I set it up so that in the onTouch method of the RelativeLayout's onTouchListenner I call a method built into the GLSurfaceView that updates the translation and rotation coordinates for my drawing.
I have everything working fine, except for the fact that touch events are only triggered if the user moves his or her finger on the RelativeLayout.
I would like it to feel a little bit like a joystick: if you leave your finger pressed on the top of the RelativeLayout, then you will keep going "up" ( or more programatically: the event.getX() and event.getY() that were sent last should get looped, or re-sent, in the GLSurfaceView until the user either moves his finger inside the RelativeLayout, or stops touching the RelativeLayout altogether.)
What should I use to detect whether or not the RelativeLayout is currently being touched (even if there is no motion in the said touch)?
Thanks!
So this is what I came up pretty much simultaneously with csmcklvey
if(event.getAction()==MotionEvent.ACTION_UP)
gM.isTouchedL = false;
else
gM.isTouchedL = true;
return true;
Where .isTouchedL is the "control boolean" I use in the GLSurfaceView
Green lit csmc's answer anyways! :)
I have used rotation animation in one of my apps, which is using the onTouchEven of view. Major function has been performed in event "ACTION_MOVE". It will help you, to get through.
public boolean onTouch(View v, MotionEvent event)
{
final float xc = volumeButton.getWidth() / 2;
final float yc = volumeButton.getHeight() / 2;
final float x = event.getX();
final float y = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
// volumeButton.clearAnimation();
// mCurrAngle = Math.toDegrees(Math.atan2(x - xc, yc - y));
break;
}
case MotionEvent.ACTION_MOVE:
{
mPrevAngle = mCurrAngle;
mCurrAngle = Math.toDegrees(Math.atan2(x - xc, yc - y));
animate(mPrevAngle, mCurrAngle, 100);
break;
}
case MotionEvent.ACTION_UP :
{
mPrevAngle = mCurrAngle = 0;
break;
}
}
return true;
}
You might be able to use the event.getAction() method of the MotionEvent parameter of your onTouch() method. You can check to see if it is MotionEvent.ACTION_DOWN or MotionEvent.ACTION_UP.
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//start something
}
else if (event.getAction() == MotionEvent.ACTION_UP) {
//stop something
}
My thought is that you might be able to start or stop a thread in this manner but either way this might at least give you some more information about when the user actually presses and lifts his/her finger.

SemiClosedSlidingDrawer onClick not dispatched when semi-collapsed

i'm using a SemiClosedSlidingDrawer (http://pastebin.com/FtVyrcEb) and i've added on content part some buttons on the top of slider which are always visibles.
The problems is that they are clickable (or click event is dispatched) only when slider is fully opened... When slider is "semi-opened" click event not seems dispached to button... I have inspected with debugger into onInterceptTouchEvent() and in both cases (opened/semi-collapsed) the following code
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mLocked) {
return false;
}
final int action = event.getAction();
float x = event.getX();
float y = event.getY();
final Rect frame = mFrame;
final View handle = mHandle;
handle.getHitRect(frame);
//FOLLOWING THE CRITICAL CODE
if (!mTracking && !frame.contains((int) x, (int) y)) {
return false;
}
return false but only when slider is opened event was dispached...
It checks if a (x,y) relative to the click are contained in a rectangle created starting from the HandleButton view of sliding drawer...
final Rect frame = mFrame;
final View handle = mHandle;
handle.getHitRect(frame);
and this is obviously false because i'm clicking on a button contained inside the content part of slidingdrawer and that's ok...
As i said above the problem is that in semi-collapsed state, buttons contained in content part are not receiving the event...
Have you any idea how can i solve this issue?
Can be some state of slidingdrawer that avoid to click childs when collapsed?
Thanks in advance...
Right, I think I've figured out a way to do this.
First you need to modify onInterceptTouchEvent() to return true whenever the user presses the visible content during the semi-opened state. So, for instance, if your SemiClosedSlidingDrawer view is located at the very bottom of the screen, you can use a simple detection algorithm, like this:
public boolean onInterceptTouchEvent(MotionEvent event) {
...
handle.getHitRect(frame);
// NEW: Check if the user pressed on the "semi-open" content (below the handle):
if(!mTracking && (y >= frame.bottom) && action == MotionEvent.ACTION_DOWN) {
return true;
}
if (!mTracking && !frame.contains((int) x, (int) y)) {
...
}
Now the touch events during the user's interaction with the semi-opened content will be dispatched to onTouchEvent(). Now we just need to intercept these events and "manually" redirect them to the right view (note that we also need to offset the coordinates for the child view):
public boolean onTouchEvent(MotionEvent event) {
...
if (mTracking) {
...
}
else
{
// NEW: Dispatch events to the "semi-open" view:
final Rect frame = mFrame;
final View handle = mHandle;
handle.getHitRect(frame);
float x = event.getX();
float y = event.getY() - frame.bottom;
MotionEvent newEvent = MotionEvent.obtain(event);
newEvent.setLocation(x, y);
return mContent.dispatchTouchEvent(newEvent);
}
return mTracking || mAnimating || super.onTouchEvent(event);
}
It's a bit of a messy implementation, but I think the basic concept is right. Let me know how it works for you!

Identify single touch vs move touch on Android

I'm developing a game with 2 functions one is called singleClick() and the other
moveClick(float dx, float dy)
singleClick() is called from MotionEvent.ACTION_UP
moveClick() is called from MotionEvent.ACTION_MOVE (where dx = x- previousX)
My problem: If I try a move action with my finger, the single click is also called. The opposite is also the same, when I single click something with my finger, the moveClick() is called.
I can handle the moveClick() being called at single click but the singleClick() calls when player is moving really screws up my game controls.
How can I know which is which??
I'm thinking this may have something todo with consuming the touch events. In your onclick listener keep in mind that if you return true that signals you have consumed the event, if you return false that event will continue to fall through and trigger the next listener.
Well if you don't want to call singleClick() when the player is moving where he touches, then you can't call singleClick from ACTION_UP, because ACTION_UP gets called each time. Maybe add a flag that keeps track of whether its a move event. If the total euclidean distance, sqrt(dx^2 + dy^2) > threshold, then change the flag isMove to true. If the total distance is less than that threshold, then call singleClick() in your ACTION_UP.
You might also want to think about timing how long the touch event is, or the total absolute distance (i.e. if someone draws a circle and comes back to the first point, my previous method would register as a single click). Think about how you want the flag to work.
I was hoping for an elegant solution, maybe something built-in with android development.. but I guess there is none so I took Benoir's advice and wrote this very not-elegant code (which seems to work well enough)
long millisLastTouch=0, millisDif,millisStartTouch=0;
final long minMillisSm = 200, minMillisBig = 300;
boolean isMove = true;
public void run(){//my main thread/loop
.....
millisDif = System.currentTimeMillis() - millisLastTouch;
if (!isMove && millisDif>minMillisBig) {
G.currentScreen.singleClick(mPreviousX,mPreviousY);
isMove = true;//to prevent multiple single clicks
}
.....
}//end of main loop
public boolean onTouchEvent(MotionEvent e) {
float x = e.getX(); float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: { }//do nothing
case MotionEvent.ACTION_UP:{
mPreviousX = x; mPreviousY = y; }
case MotionEvent.ACTION_MOVE:{
boolean newAction;
millisDif = System.currentTimeMillis() - millisLastTouch;
if (millisDif> minMillisSm) {
newAction = true;
millisStartTouch = System.currentTimeMillis();
}
else newAction = false;
millisLastTouch = System.currentTimeMillis();
millisDif = System.currentTimeMillis() - millisStartTouch;
if (newAction == false && millisDif > minMillisSm){
float dx = x - mPreviousX; float dy = y - mPreviousY;
G.currentScreen.clickMove(x,y,dx,dy);
isMove = true;
}
else isMove = false;
mPreviousX = x; mPreviousY = y;
}
}//switch
return true;
}//onTouchEvent
It's time-based only, not distance. If the user touches screen for enough time it will translate as a move action. Otherwise, it remembers the x and y position and will call singleClick() from the main thread

cannot make full functionality of lock slider with the use of seek bar

converted android seek bar to lock slider but how to disable progress change when user single tap on the Seekbar in Android
simply read the event type and if the event is of type drag move the slider
#Override
public boolean onTouchEvent(MotionEvent event)
{
int x = (int)event.getX(); // get the X position of the drag event
int y = (int)event.getY(); // get the Y position of the drag event
if(event.getAction() == MotionEvent.ACTION_MOVE){
slider.x = ... //move slider
}
...
return true;
}
hope this helps ! :)

Categories

Resources