Can someone walk me through the best way to do this? I want to make an interface that extends OnTouchListener. Instead of one meathod called onTouch(View view, MotionEvent e) i want 3 meathods; onPress() onMove() and onRelease(). The onPress meathod gets called when you press on the screen and the onMove gets called when you move your finger across the screen. onRelease gets called when you release your finger. All relevant answers are welcome.
You'd use the YourTouchListener provided by duffymo by extending your View class and adding a setYourTouchListener(YourTouchListener listener) method to this class.
You'd then override onTouchEvent(MotionEvent event) and call the relevant methods of your listener. Like so:
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
listener.onPress(this, event);
break;
case MotionEvent.ACTION_MOVE:
listener.onMove(this, event);
break;
case MotionEvent.ACTION_UP:
listener.onRelease(this, event);
break;
}
//this means that you have "used" this event. The ViewGroup will direct all further associated events straight here.
return true;
}
I would probably do essentially what CaspNZ posted, the only difference being that you shouldn't extend OnTouchListener in this case. Implementing an interface means that you must give an implementation for all of its methods, so in this case onTouch in addition to the three that you are creating, which is redundant. And of course if you still end up needing onTouch for something, you can always implement OnTouchListener in addition to YourTouchListener.
You can use GestureListener and use onFling(...) method, which gives you possibility (MotionEvent e1 and MotionEvent e2) to measure initial touch (on press) and final touch (release) and based on this you do your job. Also provides velocity of MotionEvent along x and y axis, measure pressure applied on the screen, etc. This will cut your time on writing the whole interface you are about to do.
Related
I am developing a game for android with libgdx and I have a little problem, I want to make it trigger a function when the user swipes, this I achieve. The problem is that it runs the function when you finish swiping (when you lift your finger). How can I make the function run while the swipe is doing?
This is the current code for the gesture listener:
private static class DirectionGestureListener extends GestureAdapter{
DirectionListener directionListener;
public DirectionGestureListener(DirectionListener directionListener){
this.directionListener = directionListener;
}
#Override
public boolean fling(float velocityX, float velocityY, int button) {
if(Math.abs(velocityX)>Math.abs(velocityY)){
if(velocityX>0){
directionListener.onRight();
}else{
directionListener.onLeft();
}
}else{
if(velocityY>0){
directionListener.onDown();
}else{
directionListener.onUp();
}
}
return super.fling(velocityX, velocityY, button);
}
}
And the game scene:
Gdx.input.setInputProcessor(new SimpleDirectionGestureDetector(new SimpleDirectionGestureDetector.DirectionListener() {
#Override
public void onUp() {
/*something*/
}
#Override
public void onRight() {
/*something*/
}
#Override
public void onLeft() {
/*something*/
}
#Override
public void onDown() {
/*something*/
}
}));
I looked into GestureDetector.java of libgdx source code itself. fling() will be executed whenever event of touchUp() happened. Thus aligned with what you experienced.
I think an option to make this work is to implement such gesture detection yourself by extending InputAdapter class or implementing InputProcessor interface class, then work on touchDown(), and touchDragged() to have an intended effect as you aimed for.
The idea is to keep track of touch id as it firstly touched on the screen inside touchDown() (its 3rd parameter is int button which is what you're looking at), then use that id to check and further operate inside touchDragged(). If id matches, then for simple approach, you can check whether user touched and moved for pre-defined distance by comparing it against original touching position.
Let's say we want a swipe gesture only when user firstly touches the screen, moves it for (at least) distance we pre-defined set, and within pre-defined duration. So if user firstly touches the screen, moves finger around but still not more than pre-defined distance, then finally moves far enough within duration we've set, this still won't be treated as swipe as our conditions set that it must be from first intention (first touch) to do such gesture. This means we calculate distance against original touching point, not moved point. Of course, you can customize the conditions to suit your need too.
From above, conditions to regard it as swipe gesture can include following (you can adapt these yourself)
distance it moved compared to original touching point
duration it took to move (might be 150ms, etc), longer duration user can be more relaxing not to act fast to make it registered as swipe gesture
only 1 swipe at a time can be taken into effect i.e. if user uses 2 fingers (2 touches) to swipe at the same time, only first one (or last one) can be used as swipe effect, etc.
Treating it separately for x, y direction is per your need. If so, you have to add handling code inside touchDragged() to check for both direction and send you corresponding event i.e. swipe-x, or swipe-y per se. Then you hook it up by calling one of your method to let your game knows.
i create a system overlay app using this way
but i have a problem .... when i move my button to corner of screen, i can't touch system's view like Call button in the following image
image
how can i disable any touch in my button ? ( ignore my view's touch and touch system's view )
in somewhere i find this code but it isn't work
bl.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
Always visit developer.android.com first, it's really well documented with fundamental concepts.
The onTouch method will pass the event to the layer below it, if it returns false.
If you've extended a default touchable View class, you should use return super.onTouch()
Here's the link you're looking for:
https://developer.android.com/reference/android/view/View.OnTouchListener.html
I'm very new to Android programming, and trying to understand touch events with nested views. To start, here's a description of my app:
I have a relative layout that I've added via the GUI editor. Everything is default. I've also created a class called ClipGrid that extends ScrollView. Nested inside that, I make a HorizontalScrollView. Inside of that, I make a TableLayout and it's rows. The rows contain buttons.
The end result is a grid of buttons. It displays 4x4 at once, but can scroll either direction to display other buttons.
I call it to the screen from my main activity like this:
ClipGrid clip_grid = new ClipGrid(this);
setContentView(clip_grid);
I did that just for testing purposes, and I think I will have to change it later when I want to add other views to my relativelayout. But I think it might have implications for touch events.
in the end, I want to detect when the grid has been moved and snap the newly viewable 4x4 grid of buttons to the edge of my layout when the user lifts their finger. I'm just not sure how to go about implementing this and any help would be appreciated. Thanks.
The way touch events are handled is kind of a cascading effect that starts from the top view and goes down to the lower nested views. Basically, Android will pass the event to each view until true is returned.
The general way you could implement the onTouchEvent event of a View would be:
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean actionHandled = false;
final int action = event.getAction();
switch(action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// user first touches view with pointer
break;
case MotionEvent.ACTION_MOVE:
// user is still touching view and moving pointer around
break;
case MotionEvent.ACTION_UP:
// user lifts pointer
break;
}
// if the action was not handled by this touch, send it to other views
if (!actionHandled)
actionHandled |= super.onTouch(v, MotionEvent.event);
return actionHandled;
}
I have a Java class that pertains to mouse listeners that I'm wanting to convert over to my Android app but can't quite find the necessary events.
My Java app makes use of the following methods:
mouseClicked
mousePressed
mouseReleased
I'm wanting to do something similar however not with click events but touch events. I have come across OnTouchListener and did an override on the onTouch method.
What are the alternatives to mousePressed and mouseReleased?
Edit - (updated after Peter's response)
Are the following events correct:
ACTION_DOWN : mouseClicked
ACTION_MOVE : mousePressed
ACTION_UP : mouseReleased
EDIT - 2 Example Source
My Activity doesn't have any OnTouchListener at the moment because I was hoping I could keep all the touch logic in my View.
View:
/*Inside my View - Is it proper to do onTouch logic here?
Or should I be doing this from the Activity?*/
public class myView {
public boolean onTouch(MotionEvent event) {
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//draw arrow when screen is simply touched
break;
case MotionEvent.ACTION_MOVE:
//Do Logic
break;
case MotionEvent.ACTION_UP:
//Do Logic
break;
}
}
}
The reason I am doing the logic in my View is because I have some variables that I would like to grab directly rather than creating multiple extra get methods.
Am I able to do it like this? Or will I have to override the onTouch method in my Activity and do the logic there?
All touch events (down, up, move, multitouch) are handled via 'onTouch'. See this tutorial: http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-3-understanding-touch-events/1775
If you want to register clicks on a View, implement and add an OnClickListener to it.
When you want to register touch events you need to implement the OnTouchListener and add it to that View.
UPDATE:
So I figured out that it was because the MotionEvent was being changed after I passed it to the thread, the solution was this:
/**
* Standard override to get touch screen motion events.
*/
#Override
public boolean onTouchEvent(MotionEvent event) {
MotionEvent passMe = MotionEvent.obtain(event);
event.recycle();
return thread.doTouchEvent(passMe);
}
but somehow I end up with event and passMe having the same MotionEvent id, for some reason a new MotionEvent isn't created.
What did I do wrong? :(
---vvv OLD vvv---
I'm developing an Android app and I have this code here that adds any touch inputs of action type ACTION_DOWN to a queue:
/** Adds the touch event to the motionEventQueue for processing. */
public boolean doTouchEvent(MotionEvent event) {
//Only add the MotionEvent to the queue if we care about that kind of action.
if (event.getAction() == MotionEvent.ACTION_DOWN) {
motionEventQueue.add(event);
}
return true;
}
The problem is that this code here should make sure that only MotionEvents of action type ACTION_DOWN get into the queue, but somehow I'm ending up with MotionEvents of type ACTION_UP and others inside the queue.
Why is this happening? Looking at the debugger it appears that when they're added to the queue they are of type ACTION_DOWN but when it comes time to process the queue the action types seem to have changed somehow.
EDIT: Here is my onTouchEvent method:
/**
* Standard override to get touch screen motion events.
*/
#Override
public boolean onTouchEvent(MotionEvent event) {
return thread.doTouchEvent(event);
}
EDIT: Does it have to do with the fact that I'm using more than one thread? Is it unsynchronizing or something?
Events are recycled. You're passed a reference to the event, and you're adding a reference to your queue. The event will be recycled, so your reference points to a new event that was recycled using the old event. If you're lucky.
You shouldn't use the event after the execution of onTouchEvent has finished. If you need to store some of its data persistently, copy it to your own structure (or clone the event, if that's possible).
As to your other question - only the UI thread handles touch events, so having multiple threads shouldn't matter (unless of course you use those other threads to torpedo your queue).