this problem is driving me up the wall. I'm sure I'm doing something wrong but I can't figure it out.
I have a static OnTouchListener which I attach to the TextView of a number of identical compound view objects contained within a linearlayout. This listener is in a helper class.
public static View.OnTouchListener getValueTouchListener() {
return new View.OnTouchListener() {
private int lastY;
private int lastX;
#Override
public boolean onTouch(View v, MotionEvent event) {
TextView view = (TextView)v;
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
view.setBackgroundResource(R.drawable.cog_pressed);
lastY = (int)event.getY();
lastX = (int)event.getX();
return true;
case MotionEvent.ACTION_MOVE:
//calc size of movement
int deltaY = lastY - (int)event.getY();
int deltaX = lastX - (int)event.getX();
// pass gesture up to parent if it's a big x movement - assumes user wishes to scroll parent
if (Math.abs(deltaX) > TOUCH_SLOP *2) {
return false;
}
lastX = (int)event.getX();
//process movement if larger than a touch slop
if (StrictMath.abs(deltaY) > TOUCH_SLOP) {
// reset last touch position
lastY = (int)event.getY();
//get direction of movement
int dir = (deltaY < 0)? -1 : 1;
//change index if within min and max limits
int min = (view.getTag() == Chainring.KEY)? Chainring.MIN_COG : Sprocket.MIN_COG;
int max = (view.getTag() == Chainring.KEY)? Chainring.MAX_COG : Sprocket.MAX_COG;
int value = Integer.valueOf(view.getText().toString());
if ((dir == -1 && value > min) || (dir == 1 && value < max)) {
value = value + dir;
view.setText(String.valueOf(value));
view.playSoundEffect(SoundEffectConstants.CLICK);
}
}
return true;
case MotionEvent.ACTION_UP:
view.setBackgroundResource(R.drawable.cog_unpressed);
return true;
case MotionEvent.ACTION_CANCEL:
view.setBackgroundResource(R.drawable.cog_unpressed);
return false;
default:
return false;
}
}
};
}
I hook up this listener to my TextView in a class that extends linear layout
mValueTextView.setOnTouchListener(CogPickerHelper.getValueTouchListener());
My problem is the method doesn't seem to execute the return command in the ACTION_DOWN, ACTION_UP and ACTION-MOVE cases. For example, with an ACTION_DOWN, the code executes as far as the return command but then steps directly to the default case and returns false - even though Logcat still reports the action as being ACTION-DOWN.
If I try to set a breakpoint on the return commands, IntelliJ says: "No executable code found at line X in class com.amb.GearBuddyV2.views.CogPickerHelper$2"
Does anyone know what I'm doing wrong here?
Your code seems to be working fine, the OnTouch return value is being returned correctly.
And yes, adding a break at a return statement will lead the debugger to complain about missing executable code.
Related
I'm trying to develop my own game. But I have an issue with the onTouchEvent-Method. Little explanation before showing my code. I want to be able to control a flight with my finger. It Should always follow my finger. For that I implemented the MotionEvent.ACTION_MOVE case. The flight only can move on the left side of the screen. When I touch on the right side of the screen, it should shoot a bullet. And it should be possible to fly around and shoot simultaneously. Here is my Code:
public boolean onTouchEvent(MotionEvent event) {
int maskedaction= event.getActionMasked();
switch (maskedaction) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getX() > screenX / 2 && flight.toShoot == 0) {
flight.toShoot++;
}
break;
case MotionEvent.ACTION_MOVE: {
if (event.getX() < screenX / 2 - 100) {
flight.x = (int) event.getX() - 100;
flight.y = (int) event.getY() - 50;
}
break;
}
}
return true;
}
So that's the code. When I touch the left half of the screen, I can fly around with the plane. When I Touch the right side, I can shoot. But I cannot do it at the same time. I want to touch first the left half and fly around. By clicking on the right side, at the same time as i'm moving my finger on the left side, it should shoot a bullet.
I found out that when I click at the same time on both sides with one finger, then the plane shoots one bullet. If i release now the right finger(the finger that shoots when it touches the screen) I can fly around and while I'm flying around I can shoot simultaneously.
You can detect your touch by detecting IDs, https://developer.android.com/training/gestures/multi
You can do something like this :
int leftSideId = 0;
int rightSideId = 1;
public boolean onTouchEvent(MotionEvent event) {
int maskedaction= event.getActionMasked();
// Get the pointer ID
int mActivePointerId = event.getPointerId(0);
switch (maskedaction) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getX() > screenX / 2) {
rightSideId = mActivePointerId;
if(flight.toShoot == 0){
flight.toShoot++;
}
}else{
leftSideId = mActivePointerId;
}
break;
case MotionEvent.ACTION_MOVE: {
if (event.getX() < screenX / 2 - 100) {
if (mActivePointerId == leftSideId) {
flight.x = (int) event.getX() - 100;
flight.y = (int) event.getY() - 50;
}
}
break;
}
}
return true;
}
So I found a solution by myself.
public boolean onTouchEvent(MotionEvent event){
int amount=event.getPointerCount();
for (int i=0; i<amount; i++) {
float x = event.getX(i);
float y = event.getY(i);
if (x>screenX/2 && flight.toShoot==0){
flight.toShoot++;
}
if (x<screenX/2 -100 && flyingenabled)
{
flight.x = (int) x - 100;
flight.y = (int) y - 50;
}
}
return true;
}
Thats working for my problem. Easier than I thought.
I'm creating a Pong game in android and I have a problem with multitouch
To move my two players, I'm using onTouchEvent method, and both of players can be moved in the same time. There is one problem : if the last finger down on the screen is not the first up, I have an Exception because the pointerId of the remaining finger is equals to pointerCount and my game exits. And I have to use onTouchEvent method to get coordonates x and y of all fingers on the screen.
#SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent event) {
final int actionPerformed = event.getAction();
switch(actionPerformed) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int pointerCount = event.getPointerCount();
for(int i=0; i<pointerCount; i++) {
int x = (int) event.getX(event.getPointerId(i));
int y = (int) event.getY(event.getPointerId(i));
if(y<this.height/2-joueur1.getRayon()-1) { joueur1.setX(x); joueur1.setY(y); }
else if(y>this.height/2+joueur2.getRayon()+1){ joueur2.setX(x); joueur2.setY(y); }
}
break;
case MotionEvent.ACTION_UP:
}
return true;
}
//This is the message shown when I'm releasing a finger which is not the last one down
E/MotionEvent-JNI: An exception occurred: pointerCount 1, pointerIndex 1.
E/InputEventReceiver: Exception dispatching input event.
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.pong, PID: 29258
java.lang.IllegalArgumentException: pointerIndex out of range
at android.view.MotionEvent.nativeGetAxisValue(Native Method)
If anyone can help me to resolve my problem, I would be grateful. Thanks !
To track the touch events from multiple pointers you have to use theMotionEvent.getActionIndex() and theMotionEvent.getActionMasked() methods to identify the index of the pointer and the touch event which happened for this pointer.
This pointer index can change over time, e.g. if one finger is lifted from the device. The stable version of a pointer is the pointer id, which can be determined with thegetPointerId(pointerIndex) method from the MotionEvent object.
#Override
public boolean onTouchEvent(MotionEvent event)
{
// get pointer index from the event object int
pointerIndex = event.getActionIndex();
// get pointer ID
int pointerId = event.getPointerId(pointerIndex);
// get masked (not specific to a pointer) action
int maskedAction = event.getActionMasked();
switch (maskedAction)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
{
// TODO use data break;
}
case MotionEvent.ACTION_MOVE:
{
// a pointer was moved
// TODO use data break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
{
// TODO use data break;
}
}
invalidate();
return true;
}
Thank you for your answer, but I still have this problem using getActionMasked() and getActionIndex()... This is my new code :
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
//Detection du nombre de doigts sur l'écran et deplacement des joueurs en fonction de la position des doigts
int pointerCount = event.getPointerCount();
for(int i=0; i<pointerCount; i++) {
int pointerIndex = event.getActionIndex();
int pointerId = event.getPointerId(pointerIndex);
int maskedAction = event.getActionMasked();
switch(maskedAction) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int x = (int) event.getX(pointerId);
int y = (int) event.getY(pointerId);
this.posX = x; this.posY = y;
//Déplacement du joueur 1
if (posX >= joueur1.getX()-joueur1.getRayon() && posX <= joueur1.getX()+joueur1.getRayon()
&& posY >= joueur1.getY()-joueur1.getRayon() && posY <= joueur1.getY()+joueur1.getRayon()
&& posY < this.height/2-joueur1.getRayon()) {
joueur1.setX(posX);
joueur1.setY(posY);
}
//Déplacement du joueur 2
if (posX >= joueur2.getX()-joueur2.getRayon() && posX <= joueur2.getX()+joueur2.getRayon()
&& posY >= joueur2.getY()-joueur2.getRayon() && posY <= joueur2.getY()+joueur2.getRayon()
&& posY > this.height/2+joueur2.getRayon()) {
joueur2.setX(posX);
joueur2.setY(posY);
}
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
}
return true;
Do I need to use for loop to make multitouch working ? Because only one player can be moved if I don't do this.
I'm having a very similar issue described here, except instead of using ScaleAnimation, I'm allowing pinch zoom/pan in my RelativeLayout.
The zoom/panning works perfectly, but regardless of how my view is panned/zoomed, the clickable area does not change along with the visual representation. Here's what my dispatchTouchEvent looks like:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mScaleGestureDetector != null && mGestureDetector != null) {
mScaleGestureDetector.onTouchEvent(ev);
mGestureDetector.onTouchEvent(ev);
}
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleGestureDetector.isInProgress() && mScaleFactor > 1f) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
float newPosX = mPosX + dx;
float newPosY = mPosY + dy;
if (isCoordinateInBound(newPosX, mScreenSize.x))
mPosX = newPosX;
if (isCoordinateInBound(newPosY, mScreenSize.y))
mPosY = newPosY;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return super.dispatchTouchEvent(ev);
}
and my dispatchDraw:
protected void dispatchDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
How do you modify the clickable area accordingly to modified scale/transformation of canvas?
Since you are already onverriding dispatchTouchEvent, you can try this:
manually evaluate each MotionEvent by considering the current zoom/pan transformation; you can create a new MotionEvent (let's call it FakeMotionEvent) by applying the reverse zoom/pan transformation to the original MotionEvent m.
Check if the FakeMotionEvent intercepts a specific View v; this means the user is touching in a position which represents the user-visibile position of v.
If FakeMotionEvent intercepts v, consume the current MotionEvent and invoke v.dispatchTouchEvent(m);
TIP: You can use the method below to evaluate if a MotionEvent intercepts a View with a certain degree of tolerance:
private boolean intercept(MotionEvent ev, View view, float boundingBoxTolerance){
if (boundingBoxTolerance < 1.0f) {
boundingBoxTolerance = 1.0f;
}
try {
if (ev != null && view != null) {
int coords[] = new int[2];
view.getLocationOnScreen(coords);
if (ev.getRawX() >= ((float)coords[0]) / boundingBoxTolerance && ev.getRawX() <= coords[0] + ((float) view.getWidth()) * boundingBoxTolerance) {
if(ev.getRawY() >= ((float)coords[1]) / boundingBoxTolerance && ev.getRawY() <= coords[1] + ((float) view.getHeight()) * boundingBoxTolerance)
return true;
}
}
}
catch (Exception e) {}
return false;
}
I guess you are using View Animations, i will prefer to use Property animations instead
excerpt from Android documentation (see highlighted)
The view animation system provides the capability to only animate View
objects, so if you wanted to animate non-View objects, you have to
implement your own code to do so. The view animation system is also
constrained in the fact that it only exposes a few aspects of a View
object to animate, such as the scaling and rotation of a View but not
the background color, for instance.
Another disadvantage of the view animation system is that it only
modified where the View was drawn, and not the actual View itself. For
instance, if you animated a button to move across the screen, the
button draws correctly, but the actual location where you can click
the button does not change, so you have to implement your own logic to
handle this.
With the property animation system, these constraints are completely
removed, and you can animate any property of any object (Views and
non-Views) and the object itself is actually modified. The property
animation system is also more robust in the way it carries out
animation. At a high level, you assign animators to the properties
that you want to animate, such as color, position, or size and can
define aspects of the animation such as interpolation and
synchronization of multiple animators.
The view animation system, however, takes less time to setup and
requires less code to write. If view animation accomplishes everything
that you need to do, or if your existing code already works the way
you want, there is no need to use the property animation system. It
also might make sense to use both animation systems for different
situations if the use case arises.
onTouch method of my View:
public boolean onTouch(View view, MotionEvent event) {
Log.d("Touch", "Touch");
int mNewX = (int) Math.floor(event.getX());
int mNewY = (int) Math.floor(event.getY());
boolean isPositionFree = isPositionFree(mNewX, mNewY);
if (!isPositionFree) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int i = 0;
for (Point point : points) {
if (point.spotted) {
points.remove(i);
invalidate();
break;
}
i++;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
int i = 0;
for (Point point : points) {
if (point.spotted) {
points.remove(i);
Point p = new Point(mNewX, mNewY, point.TYPE);
points.add(i, p);
invalidate();
break;
}
i++;
}
}
}
}
There are multiple items in the canvas. Their positions are saved in "points". They get drawn to the canvas in a onDraw method via the position of those "points", means Point point.x and point.y.
Now, when I click an item (a point on the canvas), it should disappear.
Then, when the MotionEvent.ACTION_MOVE is true, I want to move the point, depending on the event.getX() and event.getY().
the method "isPositionFree(newX,newY)" checks if the point.x and point.y equals newX and newY (the position I just touched on the screen).
if the position is taken (means, there is an item where I just clicked), I'll get to the motionevent-IFs.
Here comes the problem:
my code removes the point before I can actually move it. I didnt find any way I could fix this problem for hours. :/ I find it difficult, since the onTouch is always called from the beginning, means ACTION_DOWN and ACTION_MOVE never take place at the same time.
Do you know any fix for this?
thanks in advance, Sebastian
Got it to work!
For everbody having the same issue, feel free to take this sample code as a help :)
Stuff I declared in the beginning
Vector<Point> points = new Vector<Point>();
Bitmap[] monsterTypes = new Bitmap[3];
Vector<Integer> distanceMovedX = new Vector<Integer>();
Vector<Integer> distanceMovedY = new Vector<Integer>();
int mNewX = -1;
int mNewY = -1;
OnTouch-Method
public boolean onTouch(View view, MotionEvent event) {
mNewX = (int) FloatMath.floor(event.getX());
mNewY = (int) FloatMath.floor(event.getY());
boolean touchedPoint = touchedPoint(mNewX, mNewY);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
distanceMovedX.add(mNewX);
distanceMovedY.add(mNewY);
break;
case MotionEvent.ACTION_UP:
isMoveEvent = isMoveEvent();
if (isMoveEvent) {
for (Point point : points) {
if (point.spotted) {
// Your code
}
i++;
}
} else {
if (touchedPoint) {
for (Point point : points) {
if (point.spotted) {
// Your code
}
}
}
}
distanceMovedX.clear();
distanceMovedY.clear();
return true;
}
return true;
}
touchedPoint-Method
public boolean touchedPoint(int mNewX, int mNewY) {
boolean touchedPoint = false;
int height = 0;
int width = 0;
for (Point point : points) {
height = monsterTypes[point.TYPE - 1].getHeight();
width = monsterTypes[point.TYPE - 1].getWidth();
if (point.x + width < mNewX || point.x > mNewX + width
|| point.y + height < mNewY || point.y > mNewY + height) {
touchedPoint = false;
point.spotted = false;
} else {
touchedPoint = true;
point.spotted = true;
return touchedPoint;
}
}
return touchedPoint;
}
isMoveEvent-Method
public boolean isMoveEvent() {
boolean isMoveEvent = false;
boolean isMoveEventX = false;
boolean isMoveEventY = false;
for (int i = 0; i <= (points.size() -1); i++) {
Log.d("point", "for loop entered");
if (!distanceMovedY.isEmpty()) {
Log.d("point.x", "distanceMovedY is not empty");
int dMY = distanceMovedY.get(distanceMovedY.size() - 1) - distanceMovedY.get(0);
if ((dMY > 50 || dMY <= 0) && dMY != 0) {
Log.d("point.y", "is move event");
Log.d("point.y", "dMY: " + dMY);
isMoveEventY = true;
} else {
Log.d("point.x", "is no move event");
Log.d("point.x", "dMY: " + dMY);
isMoveEvent = false;
return isMoveEvent;
}
}
if (!distanceMovedX.isEmpty()) {
Log.d("point.x", "distanceMovedX is not empty");
int dMX = distanceMovedX.get(distanceMovedX.size() - 1) - distanceMovedX.get(0);
if (dMX <= 50 && dMX >= -50 && dMX != 0) {
Log.d("point.x", "is move event");
Log.d("point.x", "dMX: " + dMX);
isMoveEventX = true;
} else {
Log.d("point.x", "is no move event");
Log.d("point.x", "dMX: " + dMX);
isMoveEvent = false;
return isMoveEvent;
}
}
if (isMoveEventX && isMoveEventY) {
Log.d("point", "is move event");
isMoveEvent = true;
return isMoveEvent;
}
}
Log.d("point", "is no move event");
return isMoveEvent;
}
Point Class
class Point {
int x, y;
int TYPE;
boolean spotted;
boolean halfSpotted;
public Point() {
}
public Point(int x, int y, int t) {
this.x = x;
this.y = y;
this.TYPE = t;
}
#Override
public String toString() {
return x + ", " + y;
}
}
EXPLANATION:
Point:
we got a class Point. All those points declared in the Vector are x- and y-coordinates on your canvas. They help us to check the position we clicked.
monsterTypes:
its the different graphics I use. If you only use one graphic that you draw onto the canvas, change it to your needs
distanceMovedX & Y:
saves all the X and Y Coordinates of your "ACTION_MOVE". From pos 0 (the first touched point) to pos Z (the last touched point, where ACTION_UP occurs). Though its not the original X and Y position. Its the result of posZ - pos0.
With these values you can determine, after what distance travelled you wanna invoke "onMove" and BELOW which distance "onClick" should be invoked.
mNewX & Y:
the currently position of your onTouch-Method. Everytime you move your finger, newX & Y become overwritten.
Methods:
onTouch():
First, we'll overwrite mNewX and Y to the current position touched. Then we check if we clicked on an existing spot (in my case some 48px*48px area)
Next we record the taken distance in ACTION_MOVE.
After that we continue with ACTION_UP, where we check if we just performed some moveEvent or clickEvent.
touchedPoint():
calculates if we touched some existing point on the canvas, or not. returns true or false
isMoveEvent():
checks if we moved the certain distance. in my case i wanna move down, 50px or more. though im not allowed to move sidewards -50px or +50px. If its NOT a move event, the last spot touched still has to be on the in the giving distance (in my case in the 48px*48px range of the point).
Thats it. took me days to only figure out that option ;/ ashamed of that ... though I coded it pretty fast, what makes me feeling better again :D
I'm just suggesting some walk-around :
Instead of removing the point when clicking it,
make a privata Point in your class, where you currently remove the point, just set your new Variable Point to the Point you would remove...
Then, after using it in action or action move, the last place you would use it,
check if your private variable is not null, if so, remove it, then, set it to null.
try using a switch case statement:
switch finger_action:
case(ACTION_MOVE)
{
//move code
return false;
}
case(ACTION_TOUCH)
{
//dissappear code
return false;
}
note the up above code is pseudo code, but what it does it checks to see if you are moving the dot before just touching it. this way the dot will attempt a move first instead of being removed first.
thanks,
Alex
here is my code now:
public boolean onTouchEvent(MotionEvent event)
{
int action = event.getAction();
switch(action)
{
case MotionEvent.ACTION_DOWN:
// Do click work here ...
Y -= 10;
Yopen = 1;
break;
case MotionEvent.ACTION_UP:
// Do release work here ...
Yopen = 0;
break;
}
return super.onTouchEvent(event);
}
But I want to make only three different areas to perform different code.
Can some one help me, some good tutorial on the internet.
Add this code to your onTouchEvent to figure out where the user touched and respond appropriately:
//Grab the current touch coordinates
float x = event.getX();
float y = event.getY();
//If you only want something to happen then the user touches down...
if (event.getAction() != MotionEvent.ACTION_UP) return true;
//If the user pressed in the following area, run it's associated method
if (isXYInRect(x, y, new Rect(x1, y1, x2, y2)))
{
//Do whatever you want for your defined area
}
//or, if the user pressed in the following area, run it's associated method
else if (isXYInRect(x, y, new Rect(x1, y1, x2, y2)))
{
//Do whatever you want for your defined area
}
Here's the isXYinRect method:
//A helper method to determine if a coordinate is within a rectangle
private boolean isXYInRect(float x, float y, Rect rect)
{
//If it is within the bounds...
if (x > rect.left &&
x < rect.right &&
y > rect.top &&
y < rect.bottom)
{
//Then it's a hit
return true;
}
//Otherwise, it's a miss
return false;
}