How can I know the view is in touch state in Android - android

How can I know the view is in touch state.
If more than one touch points on one view,How can I catch the event of the last up touch point.
Please help?

You can override onTouchEvent() on your View. ACTION_DOWN will be given when the first "pointer" is placed. From then on, you will get ACTION_POINTER_DOWN or ACTION_POINTER_UP as subsequent fingers are pressed down and then released. Then, when the last pointer/finger is released, your View will get ACTION_UP. This is spelled out clearly in the MotionEvent docs.
Something like this might be what you're looking for, just subclass whatever View you are working with.
#Override
public boolean onTouchEvent(MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_DOWN)
isTouching = true;
else if(event.getAction() == MotionEvent.ACTION_UP)
isTouching = false;
return super.onTouchEvent(event);
}

Related

Why does an onTouchListener not report MotionEvent.ACTION_UP until an onClickListener is specified?

I have discovered some weird functionality when using android...
If I specify a ConstraintLayout's onTouchListener like so:
ConstraintLayout clSend = fragment.findViewById(R.id.alert_message_send_layout);
clSend.setOnTouchListener((view, event) -> {
Log.d("onTouch event: %d", event);
return view.onTouchEvent(event);
});
Then I press the layout, I get the result:
onTouch event: 0 // MotionEvent.ACTION_DOWN
Which is expected, however when holding on the press and releasing after a short period (2-3 seconds), onTouch() is only called for the ACTION_DOWN event. I would expect there to be a MotionEvent.ACTION_UP event but no...
But it gets weirder...
If I repeat the above but add an onClickListener like so:
ConstraintLayout clSend = fragment.findViewById(R.id.alert_message_send_layout);
clSend.setOnClickListener(v -> {});
clSend.setOnTouchListener((view, event) -> {
Log.d("onTouch event: %d", event);
return view.onTouchEvent(event);
});
Then I get both an ACTION_DOWN event, and an ACTION_UP event.
Does anyone have an explanation for this?
I've checked the Documentation:
View.OnTouchListener and
MotionEvent
I've checked the android issue tracker
I've checked here on StackOverflow.com but nothing explains it...
"One final search to make sure I'm not being lazy..."
#Danpe answers it perfectly:
MotionEvent.ACTION_UP Won't get called until the MotionEvent.ACTION_DOWN occurred, a logical explanation for this is that it's impossible for an ACTION_UP to occur if an ACTION_DOWN never occurred before it.
So all that has to be done is:
...
#Override
public boolean onTouch(View v, MotionEvent event) {
...
if (event == MotionEvent.ACTION_DOWN) {
return true;
}
...
}

Looking for a listener that differentiates between up and down events

I apologise for the somewhat noobish question.
I've tried googling it, searching this site and the android developer site, yet didn't manage to find an answer.
I'm looking for a listener that differentiates between up and down events. Currently I'm using OnTouchListener, but it gets triggered very fast, causing a noticable lag, even when I immediately return after an event that is not Down and Up.
I've also tried OnClickListener, but it seems to only get triggered when you lift your finger up, rather than have seperate events for down click and up click like I need.
Would appreciate some help,
Thanks!
YourBtn.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event){
if(event.getAction() == MotionEvent.ACTION_UP){
//do stuff
}
else if(event.getAction() == MotionEvent.ACTION_DOWN){
//do stuff
}
else if(event.getAction() == MotionEvent.ACTION_CANCEL){
//do stuff
}
return true; //return false can be used it depends
}
});
P.S: it can be: a button, a textview, an imageview or any UI element
I strongly recommend to always add ACTION_CANCEL to avoid any misbehaviours or crashes
See this:
https://developer.android.com/training/graphics/opengl/touch.html
Override the following method on your activity, then you can treat each case, when the user touch the screen, and when he take of his finger.
#Override
public boolean onTouchEvent(MotionEvent e) {
// MotionEvent reports input details from the touch screen
// and other input controls. In this case, you are only
// interested in events where the touch position changed.
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
// do your stuff
break;
case MotionEvent.ACTION_UP:
// do your stuff
break;
}
return true;
}
You should look at GestureDetector to simplify your touch event handling.

Android getting ACTION_UP without ACTION_DOWN

Hi i want to pass the touch event to the parent, only if the touch has moved, when the user clicks i want to handle it within the child, so i tried this within the child:
private boolean moved = false;
#Override
public boolean onTouchEvent(MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN) {
super.onTouchEvent(event);
moved = false;
return false;
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
moved = true;
return false;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
return !moved;
}
return true;
}
but when i return false on ACTION_DOWN i do not get ACTION_UP
On the time ACTION_DOWN occours i do not know if i will handle it or not
According to this official Android developer site link (Read Capturing touch events for a single view section)
Beware of creating a listener that returns false for the
ACTION_DOWN event. If you do this, the listener will not be called
for the subsequent ACTION_MOVE and ACTION_UP string of events.
This is because ACTION_DOWN is the starting point for all touch
events.
"If you return true from an ACTION_DOWN event you are interested in the rest of the events in that gesture. A "gesture" in this case means all events until the final ACTION_UP or ACTION_CANCEL. Returning false from an ACTION_DOWN means you do not want the event and other views will have the opportunity to handle it. If you have overlapping views this can be a sibling view. If not it will bubble up to the parent."
- What is meaning of boolean value returned from an event-handling method in Android
When you return false you don't consume touch event and it is passed to the parent (and as you've found out by experiment you can't proceed on that gesture), if you return true - you take responsibility of processing.
So try this
private boolean moved = false;
#Override
public boolean onTouchEvent(MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// CALL YOUR PARENT onTouch() IF YOU NEED TO
moved = false;
}
else if (event.getAction() == MotionEvent.ACTION_MOVE)
moved = true;
else if (event.getAction() == MotionEvent.ACTION_UP)
return !moved;
return true;
}

Android: using onTouchEvent with a custom view in a custom viewgroup

I have a custom view which I call "Node" that is a child of a custom ViewGroup called "NodeGrid". The "NodeGrid" class more specifically extends RelativeLayout.
I have the following code snippet in my custom view class ("Node"):
private boolean isBeingDragged = false;
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
isBeingDragged = true;
}
else if (event.getAction() == MotionEvent.ACTION_UP)
{
isBeingDragged = false;
}
else if (event.getAction() == MotionEvent.ACTION_MOVE)
{
if (isBeingDragged)
{
float xPosition = event.getX();
float yPosition = event.getY();
//change the x and y position here
}
}
return false;
}
The problem:
After having set breakpoints in this code, it seems like onTouchEvent is getting called only for the MotionEvent.ACTION_DOWN case, but not for either of the other two cases ("action up" or "action move"). Does anyone know of anything off hand that could be causing this to happen?
Also (could be related):
Does it matter how the view is added to the ViewGroup? I noticed that in addition to "addView" there are other methods for adding children to a ViewGroup such as "addFocusables" and "addTouchables". Right now I am simply adding the child view to the ViewGroup using "addView".
From the SDK Documentation:
onTouch() - This returns a boolean to indicate whether your listener consumes this event. The important thing is that this event can have multiple actions that follow each other. So, if you return false when the down action event is received, you indicate that you have not consumed the event and are also not interested in subsequent actions from this event. Thus, you will not be called for any other actions within the event, such as a finger gesture, or the eventual up action event.
You need to return true when the ACTION_DOWN event is triggered to indicate that you are interested in the subsequent calls relating to that same event.
HTH

Overriding onTouchEvent competing with ScrollView

From a simplistic overview I have a custom View that contains some bitmaps the user can drag around and resize.
The way I do this is fairly standard as in I override onTouchEvent in my CustomView and check if the user is touching within an image, etc.
My problem comes when I want to place this CustomView in a ScrollView. This works, but the ScrollView and the CustomView seem to compete for MotionEvents, i.e. when I try to drag an image it either moves sluggishly or the view scrolls.
I'm thinking I may have to extend a ScrollView so I can override onInterceptTouchEvent and let it know if the user is within the bounds of an image not to try and scroll. But then because the ScrollView is higher up in the hierarchy how would I get access to the CustomView's current state?
Is there a better way?
Normally Android uses a long press to begin a drag in cases like these since it helps disambiguate when the user intends to drag an item vs. scroll the item's container. But if you have an unambiguous signal when the user begins dragging an item, try getParent().requestDisallowInterceptTouchEvent(true) from the custom view when you know the user is beginning a drag. (Docs for this method here.) This will prevent the ScrollView from intercepting touch events until the end of the current gesture.
None of the solutions found worked "out of the box" for me, probably because my custom view extends View, not ViewGroup, and thus I can't implement onInterceptTouchEvent.
Also calling getParent().requestDisallowInterceptTouchEvent(true) was throwing NPE, or doing nothing at all.
Finally this is how I solved the problem:
Inside your custom onTouchEvent call requestDisallow... when your view will take care of the event. For example:
#Override
public boolean onTouchEvent(MotionEvent event) {
Point pt = new Point( (int)event.getX(), (int)event.getY() );
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (/*this is an interesting event my View will handle*/) {
// here is the fix! now without NPE
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
clicked_on_image = true;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (clicked_on_image) {
//do stuff, drag the image or whatever
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
clicked_on_image = false;
}
return true;
}
Now my custom view works fine, handling some events and letting scrollView catch the ones we don't care about. Found the solution here: http://android-devblog.blogspot.com.es/2011/01/scrolling-inside-scrollview.html
Hope it helps.
There is an Android event called MotionEvent.ACTION_CANCEL (value = 3). All I do is override my custom control's onTouchEvent method and capture this value. If I detect this condition then I respond accordingly.
Here is some code:
#Override
public boolean onTouchEvent(MotionEvent event) {
if(isTouchable) {
int maskedAction = event.getActionMasked();
if (maskedAction == MotionEvent.ACTION_DOWN) {
this.setTextColor(resources.getColor(R.color.octane_orange));
initialClick = event.getX();
} else if (maskedAction == MotionEvent.ACTION_UP) {
this.setTextColor(defaultTextColor);
endingClick = event.getX();
checkIfSwipeOrClick(initialClick, endingClick, range);
} else if(maskedAction == MotionEvent.ACTION_CANCEL)
this.setTextColor(defaultTextColor);
}
return true;
}

Categories

Resources