tracking all touch events - android

Situation
I want to implement a timeout in my app. If the user did not touch the screen within 2 minutes an inactivity fragment will be shown.
What i got so far
I am tracking the activity lifecycle using a custom Application implementing Application.ActivityLifecycleCallbacks. This way i know which Activity is currently on top and can access its FragmentManager to show my inactivity Fragment on timeout. A background Thread constantly checks if the last detected touch was longer ago than 2 minutes.
Problem
I dont know how to detect every touch that happens on the activity. I tried adding a OnTouchListener to activity.getWindow().getDecorView().setOnTouchListener(), but it is only called if the decorview is touched. If for instance a Button on top of it is touched my listener isn't notified. activity.getContentScene()' returns null for me, so i can't access the rootViewGroup` like that either.
Question
How can i detect any touches that happen on a Activity or the whole Application?

override dispatch touch event in your activity
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Rect viewRect = new Rect();
view.getGlobalVisibleRect(viewRect);
//or
int x= ev.getRawX();
int y= ev.getRawY();
if(/*check bounds of your view*/){
// set your views visiblity to gone or what you want.
}
//for prevent consuming the event.
return super.dispatchTouchEvent(ev);
}

Related

Android Screen Touch Action

I am working on an android project where I need to know if user is Touch Down the screen or Touch Up the screen and I frequently need this. How can I do that?
I used on Touch Event in switch case for action Up and action down but that works for only one time. I also tried the bellow code but it gives me the the value of var(int variable) for first touch only.
my code:
int var=-1;
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if(action==MotionEvent.ACTION_DOWN)var=1;
if(action==MotionEvent.ACTION_UP)var=0;
if(action==MotionEvent.ACTION_MOVE)var=1;
return super.onTouchEvent(event);
}
I need the value of var again and again.
Since onTouchEvent is the last point of touch event traversal, it might not be called if one of the children has consumed touch event already (of if you returned false for MotionEvent.ACTION_DOWN event indicating that you are not interested in this gesture at all).
For your problem I would rather use dispatchTouchEvent() instead of onTouchEvent() and track touches there.
For more information I highly recommend getting yourself familiar with talk from Dave Smith about Android Touch System

Cancel Touch Eventbutt

I am writing an very simple application with following scenario:
1) Screen A have 3 button to move on other screen's.
2) Now if I hold one button(say Button 1) and perform rapid click on other button then it launch multiple instance of other screen. Which I think should not be happened. How can prevent this.
3) and it's more weird. After move on other screen if I don't release Button 1 which was on Screen A then it still allow to perform click for rest of two button of screen A even I can see second screen.
Here it's clear launch second screen but still first screen button event working.
Any idea how can avoid such scenario.
How you are going to disable other buttons while having 1 enabled, that's an algorhytmic problem. You can try creating a boolean or control variable in your activity (and then pass the final reference of the activity to wherever you need it), or in a static context. But to answer the title of the question - you can "Cancel Touch Event" either by adding an OnTouchListener, or if you're extending class Button, you can override onTouchEvent(MotionEvent ev) method.
Using OnTouchListener will disable any previously defined touch-event behavior. You can call the actual click event from the inside by calling performClick method from your button.
//in order to use button inside OnTouchEvent, its reference must be final
//if it's not, create a new final reference to your button, like this:
final finalButton = button;
button.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouchEvent(MotionEvent ev) {
// ... additional code if necessary
if(canBeClicked) {
finalButton.performClick();
return true;
}
else return false;
}
}
Overriding onTouchEvent in a class extending Button should look something like this.
#Override
public boolean onTouchEvent(MotionEvent ev) {
// ... additional code if necessary
//here we don't really need to call performClick(), although API recommends it
//we just send the touch event to the super-class and let it handle the situation.
if(activity.canBeClicked) return super.onTouchEvent(ev);
else return false;
}
One solution that I found is to disable click listener in onPause() and enable it in onResume() . Can we have better support for this?

How to open the SlidingPanelLayout, only through button?

I'm developing an Android app with GoogleMap and I need to implement a SlidingPanelLayout in this Activity.
I want to open the SlidingPanelLayout only when a certain button is clicked, because if I drag the finger
on the GoogleMap the SlindingPanelLayout appears. So if I fix the open SlidingPanelLayout only when the button is clicked, the problem will be solved.
Are there any way to block the SlidingPanelLayout opening when I drag on the map, or something like this?
If you really want the panel to open only through a button press, you should create a class that extends SlidingPaneLayout and override the onInterceptTouchEvent() method.
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(!isOpen()){
//The map panel is being shown. We don't want the SlidingPaneLayout to handle the MotionEvent.
return false;
}
else{
//The other panel is being shown... Let the SlidingPaneLayout handle the MotionEvent as normal.
return super.onInterceptTouchEvent(ev);
}
}
Remember to use the custom SlidingPaneLayout class in your code or layout and not the regular one. Also, you should, obviously, place a button that will call the openPane() method of your custom class somewhere.
====
THE APPROACH BELOW IS NOT THE ANSWER TO THE QUESTION, BUT AN ALTERNATIVE SOLUTION
Now, if you want to let the user to freely use the GoogleMap object and let the SlidingPaneLayout open if a drag event occur in a certain region of the screen/map, you can use this approach:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//Get the user touch event position in dp units
float xTouchPosDp = ev.getX()/getResources().getDisplayMetrics().density;
if(!isOpen()){
if(xTouchPosDp < 30){
//If the panel is closed (map pane being entirely shown)
//and the touch event occur on the first 30 horizontal dp's
//Let the SlidingPaneLayout onTouchEvent() method handle the
//motion event alone (the GoogleMap object won't receive the event
//and depending on the movement, the panel will open)
return true;
}else{
//Now, if the panel is closed, but the touch event occur
//on the rest of the screen, let the GoogleMap object handle
//the motion event.
return false;
}
}
else{
//If the panel is opened, let the SlidingPaneLayout handle the
//motion event normally.
return super.onInterceptTouchEvent(ev);
}
}
Again, remember to use your custom SlidingPaneLayout class in your code/layout.
The problem with this solution is that if both panels are opened (they together fit the entire screen), you won't be able to move the map laterally.

How can I get all of touch events in my activity even in drag mode?

I want to get x and y position of every touch point in my activity even in drag mode. When I implement onTouchEvent method of my activity, it does not get all of touch events. How can I do that?
Thanks in advance,
public class MyActivity extends Activity {
#Override
public boolean onTouchEvent(MotionEvent event) {
// my Code
return super.onTouchEvent(event);
}
}
This onTouchEvent method just rises when you touch around the activity.
Do you have any other View which handle touch events?
The documentation says:
Called when a touch screen event was not handled by any of the views
under it. This is most useful to process touch events that happen
outside of your window bounds, where there is no view to receive it.
So when you say you don't get any events when you are in drag mode, this implies that you're dragging some thing. So you already handling the events somewhere else and therefore the onTouchEvent() method won't be notified.
You can try to return false in the other TouchListener's onTouch() method so the event will bubble up further more.

Touch events leaking up the hierarchy despite "return true"

I have a project using a ViewPager which works correctly:
I can page from view to view by flicking left and right.
On one page of the ViewPager, I have a custom view and I am trying to add below it another view (LinearLayout) that is touch sensitive and that will not "page" the ViewPager. Like this:
<LinearLayout >
<myView />
<LinearLayout> // The touch sensitive region
</LinearLayout>
</LinearLayout>
After my view has been inflated and added to the ViewPager, I call setOnTouchListener() to attach a listener to the internal LinearLayout. The listener returns "true".
Unfortunateley when I move horizontally in that view, I move to another page.
The events I get are:
ACTION_DOWN, ACTION_MOVE (n times) and finally ACTION_CANCEL. Although I stayed within the view.
It seems that the event has gone up the view hierarchy and processed by the ViewPager.
Is there a logical explanation to this behaviour ?
I have found a workaround by intercepting events at the Activity level, overriding dispatchTouchEvent().
In there, I call super.dispatchTouchEvent() for processing all events unless the user is touching my touch sensitive view. I test if the user is on the correct page and at the right location. In that case I process the event myself.
#Override
public boolean dispatchTouchEvent (MotionEvent ev) {
if ( pager.getCurrentPosition()!=0 ) { // Test ViewPager page
super.dispatchTouchEvent(ev);
}
else {
int[] xy = new int[2];
findViewById(R.id.myTouchControl).getLocationOnScreen(xy);
int topOfView = xy[1];
if ( ev.getY() > topOfView ) {
// Custom processing
...
} else {
// Standard processing
super.dispatchTouchEvent(ev);
}
}
return false;
}
No need to override dispatchTouchEvent(). Instead, in the ACTION_DOWN event, use View.getParent().requestDisallowInterceptTouchEvent(true).
requestDisallowInterceptTouchEvent(true) requests the parent view (ViewPager) not to intercept this specific touch event. It will clear this flag after the MotionEvent ends, so you must call it in ACTION_DOWN.

Categories

Resources