How to handle touchscreen events for OpenGl programs in Android? I have checked out several posts related to this question, like-
How to intercept touchscreen events in Android OpenGL ES?
But my problem is that I am only able to handle MotionEvent.ACTION_DOWN events, even if I write the handler for other type of events, those handlers are not called.Android docs sample also uses this event only.Are the other events supported??
My onTouch handler looks something like this-
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN :
Log.d(TAG, "mode=DOWN");
break;
case MotionEvent.ACTION_UP :
case MotionEvent.ACTION_POINTER_UP :
Log.d(TAG, "mode=UP");
break;
}
return true;
}
Try doing a series of if/else statements and see if they work, then try converting it to a switch statement. Maybe take out break and instead return true.
This is how I do it:
#Override
public boolean onTouch(View v, MotionEvent event) {
final float mouseSensitivity = 0.5f;
if(event.getAction()==MotionEvent.ACTION_DOWN){
startX = event.getX();
startY = event.getY();
} else if(event.getAction()==MotionEvent.ACTION_UP){
startX = 0.0f;
startY = 0.0f;
} else if(event.getAction()==MotionEvent.ACTION_MOVE){
graphicsRenderer.rotate((startY - event.getY()) * mouseSensitivity,(startX - event.getX()) * mouseSensitivity);
startX = event.getX();
startY = event.getY();
}
return true;
}
// Where startX and startY are global int variables;
Related
As finger moves on screen i want to track continuously x and y coordinates on MotionEvent.ACTION_MOVE Event.
meanwhile i should be able to know whether x,y values are increasing or decreasing.
Is it possible to do this, any help would be appreciated .
You just have to capture the starting coordinates during the ACTION_DOWN event and compare them to the the coordinates during ACTION_MOVE
Something like:
public class MyView extends ViewGroup {
private float startX;
private float startY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float dx = startX - event.getX();
float dy = startY - event.getY();
break;
}
return true;
}
}
If you want to track it even more then you can create a helper class which tracks a sample of the events to give you a direction, etc
I'm new to android studio. In my app I have a button where I want to get the x-coord and y-coord of both where the button is clicked and released. I put the following code in the onCreate() method and I'm not sure where to go from here.
btn1.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
float x1,x2,y1,y2;
switch(event.getAction()){
case(MotionEvent.ACTION_DOWN):{
x1 = event.getX();
y1 = event.getY();
Log.d("Pressed 1", "x1=" + x1 + "y1=" + y1);
break;
}
case(MotionEvent.ACTION_UP):
y2=event.getY();
Log.d("Released 1", "x2=" + x2 + "y2=" + y2);
}
return false;
}
});
You should return true, or the following MotionEvent will not going to this onTouch callback.
If you need the moving event, case MotionEvent.ACTION_MOVE also.
I am trying the android motion event. However, I do not know why it cant go to > case MotionEvent.ACTION_MOVE:
Here is my codes.
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
startX = (int)event.getX();
startY = (int)event.getY();
selecting=false;
break;
case MotionEvent.ACTION_MOVE:
selecting=true;
break;
case MotionEvent.ACTION_UP:
if(selecting){
endX = (int)event.getX();
endY = (int)event.getY();
selected=true;
}
selecting=false;
break;
}
}
No matter how I swipe and touch, even if I remove if(selecting) condition in case MotionEvent.ACTION_UP, it always show same output.
start: {498.0, 365.0}
end: {0.0, 0.0}
selecting: false
selected: false
After I change it to
public boolean onTouchEvent(MotionEvent event)
everything is okay. Can anyone explain to me why does it happen?
If possible, help me to solve this problem too. How to get the actual point when I touch on screen?
In order to get the following MotionEvents, you must return true in the onTouchEvent method.
source
I want to catch ONLY two fingers touch (one finger touch must be NOT consumed).
Unfortunately, I need to consume the "one finger touch" in order to get the "two finger touch" events.
I hope I'm a clear.
Here my code :
#Override
public boolean onTouch(View v, MotionEvent m)
{
boolean consumed = false;
int pointerCount = m.getPointerCount();
int action = m.getActionMasked();
if(pointerCount == 2)
{
switch (action)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
consumed = true;
break;
case MotionEvent.ACTION_UP:
// Work
break;
case MotionEvent.ACTION_MOVE:
// Some stuff
break;
default:
break;
}
}
else if (pointerCount == 1 && action == MotionEvent.ACTION_DOWN)
{
// This is needed to get the 2 fingers touch events...
consumed = true;
}
return consumed;
}
I've ran into a similar problem and solved my problem by handling all touches at my top view, and propagating the appropriate actions to the views below. In my example, I had an overlay layer that needed to intercept scale gestures (a recognizer is attached in my constructor), while still propagating single touches to my map view below. I haven't sent the actual touch event to my map view, but I've handled it in my top view and sent the appropriate "panning" action to my view below.
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getPointerCount() == 2) {
if(event.getActionMasked() == MotionEvent.ACTION_DOWN){
CameraUpdate update = CameraUpdateFactory.newLatLng(initialCoords);
//attachedMapFragment.getMap().moveCamera(update);
}
scaleGestureDetector.onTouchEvent(event);
}else{
if(attachedMapFragment != null) {
if(event.getActionMasked() == MotionEvent.ACTION_DOWN){
initialCoords = attachedMapFragment.getMap().getCameraPosition().target;
lastTouchX = event.getX();
lastTouchY = event.getY();
}else if(event.getActionMasked() == MotionEvent.ACTION_MOVE){
float deltaX = event.getX() - lastTouchX;
float deltaY = event.getY() - lastTouchY;
lastTouchX = event.getX();
lastTouchY = event.getY();
Projection projection = attachedMapFragment.getMap().getProjection();
Point center = projection.toScreenLocation(attachedMapFragment.getMap().getCameraPosition().target);
Point newCenter = new Point(center.x - (int)deltaX, center.y - (int)deltaY);
LatLng newCoords = projection.fromScreenLocation(newCenter);
CameraUpdate update = CameraUpdateFactory.newLatLng(newCoords);
attachedMapFragment.getMap().moveCamera(update);
}
}
}
return true;
}
This way, I had a scale recognizer on my custom view, while the map was still panning appropriately to single touch. Actually, map view wasn't getting any touches at all, my custom view was "routing" the action as seen above. While not always ideal, this would solve the problem in many cases.
I would like to set up following layout with working scroll and click events:
My CustomSlidingDrawer.xml:
<com.project.util.CustomSliderDrawer
android:id="#+id/slidingDrawerHotelList"
android:layout_width="0dp"
android:layout_height="match_parent"
android:allowSingleTap="false"
android:content="#+id/content"
android:handle="#+id/handle"
android:orientation="horizontal" >
<RelativeLayout
android:id="#+id/handle"
android:layout_width="0dp"
android:layout_height="match_parent" >
<RelativeLayout
android:id="#+id/content"
android:layout_width="0dp"
android:layout_height="0dp" >
</RelativeLayout>
</RelativeLayout>
</com.project.util.CustomSliderDrawer>
The width of the elements are set during runtime.
Here I have to mention, that my real content is in the handle of the Drawer, and the content is only a dummy. I'm doing that in this way, because I was not able to hide the handle. Or make it invisible. So, if I want to swipe in I touch the handle (my content) and pull. In the SlidingDrawer, the handle is a placeholder for a fragment which contains a CustomListView.
What now works: I can scroll the SlidingDrawer.
What behavior I would like to gain:
On the SlidingDrawer the MotionEvents are detected in X-Axis direction (Vertical swipe). If a MotionEvent in Y-Axis direction is detected, then the Events shoud go to my CustomListView. Even the onClicks should pass through to the ListView.
What I've tried: I read a lot: here, this page and this one
Then I tried to put all the navigation control in the main activity, what makes really sense in my eyes:
MainActivity:
slidingDrawer.setOnTouchListener(new OnTouchListener() {
int downX, downY;
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int deltaX = downX - (int) event.getX();
int deltaY = downY - (int) event.getY();
if (Math.abs(deltaX) > Math.abs(deltaY)) {
Log.i(TAG, "scrollX direction");
isVerticalSwipe = true;
} else {
Log.i(TAG, "scrollY direction");
isVerticalSwipe = false;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}
});
But when I try here to return "isVerticalSwipe" as local, the SlidingDrawer works not properly anymore. :-(
The 2nd try was to intercept the touch events in the custom sliding drawer:
CustomSlidingDrawer:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "onInterceptTouchEvent ACTION_DOWN");
xDistance = yDistance = 0f;
downX = (int) ev.getX();
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "onInterceptTouchEvent ACTION_MOVE");
final int curX = (int) ev.getX();
final int curY = (int) ev.getY();
xDistance += Math.abs(curX - downX);
yDistance += Math.abs(curY - downY);
downX = curX;
downY = curY;
if (xDistance > yDistance)
return false;
}
return super.onInterceptTouchEvent(ev);
}
Here happens only the DOWN event and the MOVE event is never reached.
I also made some trys with "dispatchTouchEvent" but I got no success.
I know, that the events will pass through the "layer" of my views - from top to bottom.
So it would be perfect to know, how I can implement the construct from the first link above in my CustomSlidingDrawer (= top view) and when a vertical swipe is detected keep and handle it and if it's a horizontal swipe, then please send it to the CustomListView and perform there the events.
Any help, thoughts, ideas or code snippets are appreciated. :)
Thank you!
[EDIT]
I've made some progress. I get now my events from the SlidingDrawer down to my ListView. I can scroll and click in the list - that works fine. But unfortunately, my SlidingDrawer shows no reaction anymore. These lines are in my custom SlidingDrawer:
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
gestureDetector = new GestureDetector(new MyGestureDetector());
if (gestureDetector.onTouchEvent(event)) {
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
return false;
} else {
Log.i(TAG, "gestureDetector returned false");
return false;
}
}
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onDown(MotionEvent e) {
Log.i(TAG, "onDown");
downX = (int) e.getX();
downY = (int) e.getY();
return false;
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
float deltaX = downX - e2.getX();
float deltaY = downY - e2.getY();
if (Math.abs(deltaX) > Math.abs(deltaY)) {
Log.i(TAG, "scrollX direction");
return true;
} else {
Log.i(TAG, "scrollY direction");
return false;
}
}
}
I don't really understand why it's not working, if there is a swipe in x-direction, then my Drawer should handle the gesture, bc I return true. Instead every event is forwarded to the ListView.
I tried to run "dispatchTouchEvent" before the "onInterceptTouchEvent" but I had no success. Maybe there is a better solution instead of using a SlidingDrawer for moving views by finger?
Okay, after a lot of tears, sweat and blood I finally found a solution which works for me: I don't take a SlidingDrawer anymore and move my layout via changing the LayoutParams.
Over the "dispatchTouchEvent" I get access to my list.
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mGestureDetector.onTouchEvent(ev)) {
return true;
}
if (ev.getAction() == MotionEvent.ACTION_UP) {
Log.i(TAG, "ACTION_UP");
Log.i(TAG, "getLeft: " + getLeft());
if (getLeft() < 280) {
// run ani to left
} else {
// run ani to right
}
}
return super.dispatchTouchEvent(ev);
}
And in my GestureDetector I handle in the overridden onScroll-function the layout translation. If someone has need for the detector don't hesitate to ask, I'll share.