I have to do the drag navigation in Android. I have added a code for the drag screen navigation. Which is not as smooth as Android default. The following code gives the drag navigation from left to right and right to left but the problem is when you tap at right and then to left then too the screen navigates. What is the right way to achieve it. Do I need to work with same code with calculating the X values?
Below the code.
// On Drag Navigation
public boolean onTouch(View arg0, MotionEvent arg1) {
// Get the action that was done on this touch event
switch (arg1.getAction())
{
case MotionEvent.ACTION_DOWN:
{
// store the X value when the user's finger was pressed down
downXValue = arg1.getX();
break;
}
case MotionEvent.ACTION_UP:
{
// Get the X value when the user released his/her finger
float currentX = arg1.getX();
// Going Backward: pushing stuff to the right
if (downXValue < currentX)
{
Intent lIntent = new Intent(getApplicationContext(), previousScreen.class);
startActivity(lIntent);
finish();
}
// Going Forward: pushing stuff to the left
if (downXValue > currentX)
{
Intent lIntent = new Intent(getApplicationContext(), nextScreen.class);
startActivity(lIntent);
finish();
}
break;
}
}
}
Please specify the right way of achieving it.
Thanks in advance
You should be using a GestureListener
You probably don't want to use separate activities for this. This pattern is used to navigate within the same activity.
There are either two or three states to consider depending on how you choose to think about it. The first is the 'dragging' state when the user has a finger in the screen and is panning back and forth. The second and third are what happens when the user lets go. If the velocity when letting go is under a certain threshold, (the system uses the minimum fling velocity from ViewConfiguration) animate to the closest screen. If the velocity is over that threshold, fling to the next screen in that direction.
Take a look at the VelocityTracker, Scroller, and Interpolator classes. All of them can help you here.
Related
i'm writing an Android app for a school project that performs a different action depending on how many fingers the user taps the screen with.
Right now this action is just to display the number of pointers detected as a toast.
I'm using the getPointerCount() method, but for some reason, I get multiple toasts. For example, a three finger tap gives me toasts for two and three finger taps, a four finger tap will give me toasts for two, three and four finger taps, and so on.
I cannot for the life of me figure out why this is. A four finger tap should display ONE toast saying "4", not cycle through 2, 3 and 4! Any help would be greatly appreciated.
public boolean onTouchEvent(MotionEvent event)
{
int action = event.getAction();
switch(action & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_POINTER_DOWN:
int count = event.getPointerCount();
if (count == 2) {
finish();
}
else if (count == 4) {
Toast.makeText(this, String.valueOf(count), Toast.LENGTH_SHORT).show();
}
else if (count == 3) {
Toast.makeText(this, String.valueOf(count), Toast.LENGTH_SHORT).show();
}
break;
}
return true;
}
P.S, I have tried moving the code outside of the switch statement, and bizarrely , this causes the toasts to count up AND down!
You're thinking of the taps as discrete events, but Android MotionEvents are delivered as a stream. In order to get an ACTION_POINTER_DOWN with three fingers, it is required that you get an action pointer down with two fingers first. (If you think about it, all three fingers do not go down at the exact same time, even if this was possible you wouldn't receive them that way).
If you log every motion event with
Log.i("test", event.toString());
You should be able to see the sequence of events you are receiving, and better understand how to deal with them.
I am trying to make all my drawn Sprites dragable for a little game. It should be able to touch anywhere and the sprites should move the same distance, the finger moves.
With the following method they will move on an ACTION_MOVE event, but only very slow, a shorter distance and sometimes they dont:
addToX/Y only adds the gap to the coordinates of the sprites
#Override
public boolean onTouchEvent(MotionEvent evt){
switch(evt.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if(getHistorySize() > 0){
for(int i = 1, n = evt.getHistorySize(); i < n; i++){
int calcX = (int) getHistoricalX(i) - (int) getHistoricalX(i-1);
int calcY = (int) getHistoricalY(i) - (int) getHistoricalY(i-1);
for(Sprite sprite : spriteList) {
sprite.addToX(calcX);
sprite.addToY(calcY);
}
}
}
return true;
}
Any ideas on this?
Assuming your Sprite class is an (potentially-indirect) extension of android.view.View, then you can use setOnDragListener() to define an onDrag() override for them. Then you can use startDrag(...) on them to begin the drag. This is typically triggered by a long-press gesture on the view to be dragged, but in your case you can trigger it from within onTouchEvent() in ACTION_MOVE once (or even ACTION_DOWN). See here for more details on these methods.
Also, with respect to the code you posted, one issue with it that probably explains why it doesn't always work is that you are only using the historical points (which may or may not have accumulated on any particular call to onTouchEvent()). Whether or not getHistorySize() is greater than 0, you should still also use evt.getX() and evt.getY() on each call to onTouchEvent(). But of course, if you use the drag listener approach I suggested instead, you won't need to worry about this.
Update per comment
If you want to move all of the sprites at once, you can put the sprites into a full-screen FrameLayout and attach a GestureDetector that uses a GestureDetector.SimpleOnGestureListener to capture onScroll() callbacks and then calls scrollTo() on the FrameLayout. When the parent FrameLayout scrolls, all of its children sprites will appear to move together.
So, here's my problem: in my game I made a countdown at the start of the game, during which the user can't press the screen, so I wait until the countdown is over to enable the OnSceneTouchListener. As soon as it's enabled, the user will be able to do some stuff moving the finger (not important what).
The problem is that if the user starts moving the finger while there's the countdown, as soon as it's over the onSceneTouchEvent method starts getting inputs, but it skips the ACTION_DOWN event since the user is already moving the finger.
To prevent this I could of course use a boolean inside the onSceneTouchEvent method, but this way it would always check that for every single input. It doesn't influence much the performance, but I'd rather find another way if it's possible.
So I was thinking, is there a way in the AndEngine to obtain the touch input event (with the information of the coordinate pressed, that's what I really need), so that I could "force" the ACTION_DOWN event by simple doing what I should do with it in the method that makes the countdown?
Thanks in advance!
Here is an example, how you can get touch coordinates.
public boolean onTouchEvent(MotionEvent event) {
int myEventAction = event.getAction();
float X = event.getX();
float Y = event.getY();
switch (myEventAction) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE: {
mySprite.setPosition(X, Y);
break;}
case MotionEvent.ACTION_UP:
break;
}
return true;
}
Source of this example
I'm currently developing an air-hockey simulation for android. For the multiplayer mode I'm tracking two touch events on the screen, which works well as long as the touch points don't get to close.
When the two fingers get to close, android only recognizes one touch event, in the middle of both points.
To make it even worse, android sometimes messes up the IDs after the collision.
I already thought about estimating the next touch points ans assigning IDs manually, does anybody know a better way, or knows about somebody who already fixed this problem programmatically?
NOTE: I'm testing on a Samsung Galaxy S 3
Not necessarily a logical fix to the issue, nevertheless a possible solution to the application:
If I'm not completely mistaken, air-hockey games shouldn't allow opponents to intrude on each others game field. If we assume a thick border cross the center of the screen (in portrait mode), then I wouldn't be allowed to do anything beyond that border, hence there is no point in tracking my finger after it reaches the border line.
Encapsulating your tracked touch events into valid physical locations as described might just help you in ignoring invalid points (given that the physical locations doesn't intersect, that is).
You might also have to keep track of direction of the touch vector: if the vector is stretching from the center of the screen towards "your end" it might be the opponents intruding finger or your own returning finger. In neither case should they affect the hockey puck (perhaps).
It may depend on the device you are using, but I'm using the code below in a Huawei X5 and It never mess up fingers, even if they touch each other or it I twist them over the screen.
private static PointF touchScreenStartPtArr[] = new PointF[10];
private static PointF touchScreenStopPtArr[] = new PointF[10];
private static PointF touchScreenCurrPtArr[] = new PointF[10];
OnTouchListener onTouchListenerMulti = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int fingerId = event.getPointerId(pointerIndex);
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
touchScreenStartPtArr[fingerId].x = event.getX(pointerIndex);
touchScreenStartPtArr[fingerId].y = event.getY(pointerIndex);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
touchScreenStopPtArr[fingerId].x = event.getX(pointerIndex);
touchScreenStopPtArr[fingerId].y = event.getX(pointerIndex);
break;
case MotionEvent.ACTION_MOVE:
int pointerCount = event.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
touchScreenCurrPtArr[fingerId].x = event.getX(i);
touchScreenCurrPtArr[fingerId].y = event.getY(i);
}
break;
}
return true;
}
};
Note that I'm using fingerId and not pointerId to identify the correct finger, as pointer id may change when one finger is released.
Hope it works for you.
Here's the way I see it.
The touchscreen hardware gives you a resolution below which two touches are the same as one. This is something you cannot change.
Now the question is, what to do when two touches merge? (This is something that can be tested for programmatically one would think; e.g. if 2 touch pts -> 1 touch pt. AND prev touch pt 1 is close enough to prev touch pt 2...). In your case, I would move both pucks along the merged touch gesture until they separate, then return individual control.
Of course, I see several problems with this, like which touch controls which puck after the split? Maybe one person lifted their finger during the merge.
You could have both players lose control of their puck if a merge occurs. This could simulate the shock to the wrist as your hand bashes into your opponent's :)
I also like #dbm idea.
Hope this helps. Probably didn't :)
I have an activity filled with buttons on the screen,and each has characteer text
something like : [A] [B] [C] etc. I need to code that when the user touches and slides his/her finger on the screen, it should aggregate the text of each touched button ,sample result should be : A + B + C = ABC . I have written some code but it does not work as intended . in my code ,I keep the array of buttons called buttons to identify which button is touched at that moment , but this code just work for first touch , it is Action_Down , it realize just first button which I touch , When I slide my finger around I cannot get other buttons texts. , So what is your suggestions ? help please . .
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
for(int i=0;i<buttons.size();i++){
if(buttons.elementAt(i).getId()==v.getId()){
text+=buttons.elementAt(i).getText();
break;
}
}
break;
case MotionEvent.ACTION_MOVE:
for(int i=0;i<buttons.size();i++){
if(buttons.elementAt(i).getId()==v.getId()){
text+=buttons.elementAt(i).getText();
break;
}
}
case MotionEvent.ACTION_UP:
Toast.makeText(context,text,Toast.LENGTH_SHORT).show();
text="";
break;
}
return false;
}
I guess you think of multi touch, but your code is not.
For multi touch you need to use ACTION_POINTER_UP/DOWN. But not all devices support multi touch.
For non-multi-touch you need a more complex handling. Buttons may be selected. The join must be done explicitly.