How to show a response while gesture input is in progress?
I'm using swipe down gesture to reload my WebView.
Using this for gesture detection:How to detect swipe direction between left/right and up/down
Problem
SimpleGestureListener captures the result of user input only. It cannot be used to show a response, e.g. animation, while the user is performing a gesture.
Imperfect inelegant solution:
flaw: Shows animation independent of SimpleOnGestureListener; a response to user gesture input is displayed and gesture input may still fail or vice versa.
private volatile float userTouchY = -1;
// translating WebView every onTouch event call is inefficient.
// only translate when translateYBy value is greater than a threshold.
private float translateYBy = 0;
/**
* Shift webView without animation.
* #param num
*/
private void __shiftWebViewDownBy(int num){
float current = webView.getY();
if(current >= webviewTranslationLimit) return;
current += num;
if(current> webviewTranslationLimit) current = webviewTranslationLimit;
webView.setY(current);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
//Skip everything unless at the content top.
if(webView.getScrollY() > 0) return false;
int action = event.getAction();
if(action == MotionEvent.ACTION_MOVE){
float y = event.getY();
float dif = y - userTouchY;
if(dif < webviewTranslationLimit/10){
//dif less than screenHeight*1/40, ignore input
}else if(dif < -(webviewTranslationLimit/5)){
//swipe up
userTouchY = -1;//not swipe down cancelling
__shiftWebViewTopTo(0);
}else if(userTouchY < y) {
//swipe down
translateYBy += dif;
if(userTouchY == -1){
//userTouchY at the initial value, ignore for this once.
userTouchY = y;
}else{
userTouchY = y;
if(translateYBy > 5 ){
__shiftWebViewDownBy((int)translateYBy);
translateYBy = 0;
}
}
}
}else{
Log.d(TAG,"action not move:" +MotionEvent.actionToString(action));
// Webview shift down should only occur while moving
// Once finger is off,cancel
__shiftWebViewTopTo(0);
userTouchY = -1;
}
boolean result = gestureDetector.onTouchEvent(event);
return result;
}
Writing a GestureListener of a sort from scratch obviously solves the problem and I can simply use Toast to message the user for a failed gesture input, but there ought to be an easier, more readable solution.
Related
I have a drag and drop list implementation in Xamarin. The problem is that I want to be able to drag and drop element only when the drag and drop button is touched not the whole element.
I was trying to detect for the ImageView being pressed but I am unable to get the correct hit ImageView.
public bool OnDown (MotionEvent e) {
int x = (int) e.GetX();
int y = (int) e.GetY();
int itemnum = PointToPosition(x, y);
if (itemnum == AdapterView.InvalidPosition) {
return false;
}
if (m_dragging != null) {
m_dragging.Stop();
m_dragging = null;
}
View item = GetChildAt(itemnum - FirstVisiblePosition);
item.Pressed = false;
var dragImage = item.FindViewById<ImageView> (Resource.Id.drag_image);
Rect bounds = new Rect();
dragImage.GetDrawingRect (bounds);
if (!bounds.Contains (x, y)) {
return false;
}
The following code works only for the first element in the list and does not apply to any other. I am suspecting that the detection of hit is incorrect.
Solved followingly. I realized that I actually do not need to care about the Y axis and it's enough to take just X axis in account.
// Detect if the user is actually pressing the drag and drop image or not.
var dragImage = item.FindViewById<ImageView> (Resource.Id.drag_image);
if (dragImage != null) {
var dragImageHitRect = new Rect ();
dragImage.GetDrawingRect (dragImageHitRect);
// In this case we do not care about the Y axis. Just compare the x axis.
if (!dragImageHitRect.Contains (x, (int)dragImage.GetY ())) {
return false;
}
}
I am working on a multitouch program that needs to record only the movements made by the second finger or index pointer.
Now the documentation says that we can use MotionEvent.ACTION_POINTER_INDEX_MASK and & it with action and shift by INDEX_SHIFT to get the pointer that made the action like going up or down. But this technique does not work on move.
Is there anyway that we can detect the move action made by a certain pointer alone?
Thx,
yes, you can have something like this in your View class:
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
if(event.getPointerCount()>1){
//where 1 is the index of the second finger
final int Y = event.getY(1);
final int X = event.getX(1);
}
break;
}
}
so depending on what finger you want to get the movent you can set the get to that index. Rember that values may be from 0 (the first pointer that is down) to getPointerCount()-1.
I tested this on 2.2 Gingerbread so I hope it be useful for you :)
You can get the effective pointer index by checking which pointer changed:
private final int MAX_POINTER = 5; // 5 different touch pointers supported on most devices
private float mLastTouchPositionX[];
private float mLastTouchPositionY[];
#Override
public boolean onTouchEvent(MotionEvent aEvent)
int tActionIndex = aEvent.getActionIndex();
int tPointerCount = aEvent.getPointerCount();
/*
* Check which pointer changed on move
*/
if (tMaskedAction == MotionEvent.ACTION_MOVE) {
for (int i = 0; i < tPointerCount && i < MAX_POINTER; i++) {
if (mLastTouchPositionX[i] != aEvent.getX(i) || mLastTouchPositionY[i] != aEvent.getY(i)) {
mLastTouchPositionX[i] = aEvent.getX(i);
mLastTouchPositionY[i] = aEvent.getY(i);
// Found new action index
tActionIndex = i;
break;
}
}
}
...
}
I've been exploring into Nim Game on Android. The players are going to take objects from heaps. I use openGLES to draw the objects and heaps. Where I'm stuck is how to "take".
As the samples shown on official dev guide
, I can override onTouchEvent method in the class that extends GLSurfaceView for the interaction. However, how I could tell where the objects have been drawn? Or are there any objects at the coordinates where I touch?
Any ideas?
Thx in advance!
If I'm understanding your question correctly, it sounds like you want to do some simple collision detection to see if your touch point is inside one of the objects on the heap. You can do this with some basic math between the coordinates of the touch point and the center coordinates which you used to draw the object.
For instance, assuming your objects are rectangles, this would be the general idea:
boolean detectCollision(Object object, TouchPoint touch) {
return object.x - object.width/2 <= touch.x &&
object.x + object.width/2 >= touch.x &&
object.y - object.height/2 <= touch.y &&
object.y + object.height/2 >= touch y;
}
You could then iterate through all of the objects in your heaps and if this returns true for any of them, then you know your touchpoint is inside of that object and can proceed to call whatever you need to call on it.
Keep in mind that the touch coordinates the system gives you will be screen coordinates, so you have to take into account any discrepancies between the screen coordinate system and the coordinate system you defined with your view frustum.
public class Main extends Activity implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
synchronized (this) {
if (!_isPaused) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
_touchedX = event.getX();
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
float touchedX = event.getX();
float dx = Math.abs(_touchedX - touchedX);
_dxLowPassed = lowPass(dx, _dxLowPassed);
switch (_screenUsage) {
case HALF_SCREEN:
if (touchedX < _width / 2) {
if(touchedX < _touchedX) {
_zAngle = (2 * _dxLowPassed / _width) * TOUCH_SENSITIVITY * ANGLE_SPAN;
_zAngleLowPassed = lowPass(_zAngle, _zAngleLowPassed);
GLES20Renderer._zAngle = GLES20Renderer._zAngle + _zAngleLowPassed;
}
} else {
if( touchedX > _touchedX ) {
_zAngle = (2 * _dxLowPassed / _width) * TOUCH_SENSITIVITY * ANGLE_SPAN;
_zAngleLowPassed = lowPass(_zAngle, _zAngleLowPassed);
GLES20Renderer._zAngle = GLES20Renderer._zAngle - _zAngleLowPassed;
}
}
Log.d("TOUCH", new Float(_zAngleLowPassed).toString());
break;
After getting the calculator application to work I decided to try to create pong. There is a box in the center and two paddles on both ends. The phone is horizontal. I have the box bouncing off the walls and the paddle moves with me moving my finger down. My problem is i want to make it two player and i want to have multiple finger input for the game. I want one finger to move paddle 1 and the other to move paddle 2. So far this is my input code
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
float p1y = ev.getY();
if(ev.getX()<300)
{
player1y = p1y;
}
if(ev.getX()>300)
{
player2y = p1y;
}
//player1y = p1y;
invalidate();
break;
}
}
return true;
}
it resides in my surfaceview class. How can i modify the input method or completely get rid of it and change it to accomplish my goal? Also sorry about my variables. Eclipse crashes a lot on me and my laptops touch panel tends to move my cursor so shorter variables seemed viable. p1y is the y of the touch. and player1y and player2y is the y positions of the player1 and player2 paddle.
A MotionEvent can hold multiple pointers. Use getPointerCount() to see how many pointers are touching the screen in the current event. There are alternate versions of getX and getY that take an index from 0-getPointerCount() - 1.
In a more complex app you would want to track fingers by pointer ID, but for something this simple where you are using a cutoff point on the screen you could do something like this in your ACTION_MOVE case:
int pointerCount = ev.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
float x = ev.getX(i);
float y = ev.getY(i);
if (x < 300) {
player1y = y;
} else if (x > 300) {
player2y = y;
}
}
This post from the Android Developers Blog might help if you'd like more information: http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
Can anyone help please.
Im writing a small Android game where the player is able to select a "barrier" and drag it accross the screen with their finger. I have the barriers drawn on the screen and I am able to drag it accross the screen.
My problem however is when I add more than 1 barrier, eg 3 barriers, and drag a barrier accross the screen, they all drag and they all drag to the same position. That is to say they all lie on top of each other, making it look as though there is only 1 barrier.
Here is my code, can anyone please tell me where I am going wrong/explain where I am going wrong.
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback, SensorEventListener {
// Initialising the Barrier
private Barrier barrier[] = new Barrier[3];
// The Main Game Panel
public MainGamePanel(Context context) {
super(context);
// Adding a call-back (this) to the surfaceHolder to intercept events
getHolder().addCallback(this);
// Creating the Game items
// The starting coordinates of the Barrier
int x = 30;
int y = 270;
barrier[0] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.blue_barrier), x, y);
barrier[1] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.green_barrier), x + 15, y);
barrier[2] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.pink_barrier), x + 30, y);
// Create the Game Loop Thread
thread = new MainThread(getHolder(), this);
// Make the GamePanel focusable so it can handle events
setFocusable(true);
}
// Handles the touch events
public boolean onTouchEvent(MotionEvent event)
{
int eventAction = event.getAction();
int x = (int)event.getX();
int y = (int)event.getY();
switch (eventAction)
{
// Touch down so check if finger is on Barrier
case MotionEvent.ACTION_DOWN:
if (x > barrier[0].getX() && x < barrier[0].getX() + 8
&& y > barrier[0].getX() && y < barrier[0].getY() + 8)
{
barrier[0].isTouched();
}
else if (x > barrier[1].getX() && x < barrier[1].getX() + 8
&& y > barrier[1].getX() && y < barrier[1].getY() + 8)
{
barrier[1].isTouched();
}
else if (x > barrier[2].getX() && x < barrier[2].getX() + 8
&& y > barrier[2].getX() && y < barrier[2].getY() + 8)
{
barrier[2].isTouched();
}
break;
// Touch-drag with the Barrier
case MotionEvent.ACTION_MOVE:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i] == barrier[0])
{
barrier[0].setX(x);
barrier[0].setY(y);
} // end if
else if (barrier[i] == barrier[1])
{
barrier[1].setX(x);
barrier[1].setY(y);
}
else if (barrier[i] == barrier[2])
{
barrier[2].setX(x);
barrier[2].setY(y);
} // end else if
} // end for
break;
case MotionEvent.ACTION_UP:
// Finger no longer on Barrier - Do Nothing
break;
}
return true;
}
// Render - Draws the Game Item Bitmaps to the screen
public void render(Canvas canvas)
{
// Set the background to white
canvas.drawColor(Color.WHITE);
barrier[0].draw(canvas);
barrier[1].draw(canvas);
barrier[2].draw(canvas);
}
// Update
// This is the Game's update method
// It iterates through all the Objects and calls their update() methods (if they have one)
public void update()
{
} // end update
In your barrier move code, you aren't checking a particular barrier is touched, so you're moving all of them to the same coordinates. The following loop does exactly the same thing as your current code:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
barrier[i].setX(x);
barrier[i].setY(y);
} //end for
To fix this, you need to check if the current barrier in the loop is the one that was touched, and can change that whole loop to something like:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i].isTouched())
{
barrier[i].setX(x);
barrier[i].setY(y);
} // end if
} // end for
You would then need to make sure that you un-set the touched property in the ACTION_UP section. If you post your Barrier class definition I can probably help more.
I'm pretty sure your problem could be solved by making the barriers three separate objects instead of in an array.
As already pointed out, your code in handling the move is wrong because it moves all the Barriers to the same X,Y when it should only move the one that is touched.
Also, you are never resetting the isTouched on the objects in the Action up. When the user lifts their finger you should set them all to isTouched == false. If you don't do that then once you touch one it will always move to the X,Y.