Is there a clever way to "do something" every time a user clicks or taps on the screen? I'd like to know which view they tapped on, and do it without having to replace all my standard views with custom views.
Basically, I want to add click tracking in my app to help with analytics. We already have page tracking (by using a shared superclass for every activity), but in some cases page tracking isn't enough and we actually want to track clicks.
How can I execute a function every time the user clicks on a View on the screen?
In each of your Activities, just add this at the end of onCreate (this is especially easy if all your Activities are subclasses of a custom Activity, because then you only need to write this once):
getWindow().getDecorView().findViewById(android.R.id.content).setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent e) {
//handle your code here
return false;//don't absorb the touch.
}
});
Edit
The above code does not allow easy recognition of which Views are touched. The below code will. Note that this may break other onTouchListeners if the View in question already has registered an onTouch event. If not, this will work great. It uses the droidQuery library to select all views in the hierarchy and set uses code like above to handle touches without absorbing the event. The best place for this would be at the end of onCreate, in every Activity:
First, select the highest level of the architecture you want. If your layout's root view has an id, that would be the best thing:
$.with(this, R.id.root_id)
If not, either add one, or select the topmost view (above your layout). Note that for analytics this may provide some results you do not need:
$.with(this).selectAll()
Now, on the end of this selection, append the following:
.each(new Function() {
#Override
public void invoke($ d, Object... args) {
d.view(0).setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent e) {
//do something with the clicked view, such as:
handleTouch(v, e);
return false;//don't absorb the touch.
}
})
}
});
Then have a new method:
private void handleTouch(View v, MotionEvent e) {
//handle the view touch.
}
Related
I need to show a preference dialog that wait for Joypad keypress.
I know that DialogFragment has its own Window then has its own onKeyListener.
I can easily catch Joypad press by setting a listener like that.
public class MyDialogFragment extends PreferenceDialogFragmentCompat {
#Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
builder.setOnKeyListener(new DialogInterface.OnKeyListener() {
#Override
public boolean onKey(DialogInterface dialogInterface, int i, KeyEvent keyEvent) {
// do stuff with intercepted key press
return true;
}
});
}
}
But, I have trouble catching GenericMotionEvents. In order to intercept them, I've overridden onGenericMotionEvents in the Activity class and eventually forward the Event by calling a method on MyDialogFragment class.
It works 100% correctly when MyDialogFragment is not shown as when an analog trigger/direction stick is moved, I can get an event.
The weird part is that IF MyDialogFragment is shown, then I can get only analog direction stick events BUT NOT left/right analog triggers events.
Does someone know why and how to fix this behaviuor?
I've had a similar issue some time ago. You can use onGenericMotionEvent of Dialog or even some of the Views. It has some limitations though and works not as expected sometimes. It does work as intended though - it is just that sometimes all the generic motion events are being intercepted by something else and they aren't propagated any further anywhere - in this situation, you won't receive the callback trigger.
That is what was happening in my case and even overriding the callback method in Dialog(I haven't tried View though) failed to give me the needed result. What I did is a bit of a kludgy trick, but it did the job. I created my activity's UI as a child of one parent FrameLayout and my dialog UI was the topmost element in this FrameLayout. This trick allowed me to use the activity's native onGenericMotionEvent callback. It added some navigation handling overhead and was generally possible because of allowing UI design(without dialog shadow etc) but yeah...
Maybe some of these approaches will help you.
I have a project in which I have overridden onTouchEvent() and register taps and moves through it. I also have buttons on the screen. I'm trying to connect methods to the buttons ie:
public void onButtonClick(View view) {
System.out.println("Here");
}
I can connect the method to the button, and the connection appears in the xml:
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onButtonClick"
android:text="Button"
tools:layout_editor_absoluteX="89dp"
tools:layout_editor_absoluteY="139dp" />
But the button click does not appear to be registered. Is it because I'm also overriding onTouchEvent()? If so should I be registering clicks through MotionEvents? If so, what's a clean way to differentiate clicks coming from multiple buttons?
But the button click does not appear to be registered. Is it because
I'm also overriding onTouchEvent()?
Yes. When onTouch() method returns true, Android will consider the event has been consumed and will not pass it on to the other various touch handlers (I think it includes the onClickListener).
If so should I be registering clicks through MotionEvents?
You can do that. Or depending on your condition, simply return false from the method and let onClickListener do the job.
If so, what's a clean way to differentiate clicks coming from
multiple buttons?
onTouch(final View v, final MotionEvent event)
As you can see the onTouch() method has View type as parameter. You can check the Id of view by getting v.getId() and compare this id with the Button's Id, which you are expecting. You can do as follows:
#Override
public boolean onTouch(final View v, final MotionEvent event) {
if(v.getId()==R.id.button1){
//Click is coming from Button with id button1 (specified in layout.xml as android:id="#+id/button1").
//Do something for click
}
...
}
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?
I have a ViewPager that can contain two types of views. One of the view types has buttons, one of the view types does not. The buttonless view type does have to trap touches in onTouchEvent in order to pan and zoom an image. But I want to let clicks bubble to the ViewPager (that has a clickListener attached to it)
I have to return true in the view's onTouchEvent ACTION_DOWN or else it won't see future events.
I can't use onInterceptTouchEvent() in the ViewPager to capture clicks because one of the views does have buttons and those buttons need their clicks
So how can my view trap swipes, and let clicks bubble up?
proper way is probably to intercept only the events you want to intercept, by returning true in onInterceptTouchEvent only when needed.
but if you want to go with the dark side, there's a dirty alternative, that probably will lure you and you'll probably regret later: intercept all the events, then if needed pass them down.
e.g. have inside your views something like
public boolean canInterceptTouch(MotionEvent ev) {
// return true if you are interested in this touch event, e.g. it falls into
// a clickable area
}
and something that handle the touch event like
public void interceptTouch(MotionEvent ev) {
// here you react to the event
}
inside your viewpager you have
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
thien the view pager will handle the events in a dirty way like this:
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (yourView.canInterceptTouch(ev))) {
yourView.interceptTouch(ev);
return true;
} else {
return doSomethingElse();
}
}
note: although I've done something similar, with decent result, I do NOT reccomend it as a solution unless you've very simple logic in the handling of the motion events, otherwise it becomes a mess. Your future self will be happier if you spend some time now to do it properly.
EDIT: code has not been tested, just to give you an idea of what you need. Sorry for any typo.
Try using this ViewGroup.onInterceptTouchEvent(MotionEvent).This allows a ViewGroup to watch events as they are dispatched to child Views`
on Android! I need to get user input (touch event, keyboard event). is there any way? In java code, It seems there is no way. What about native code?
In java code, It seems there is no way.
=> sorry, there is a way to do detect any action user made and play with application.
Some examples:
KeyboardView.OnKeyboardActionListener
Responding to Touch Events
for touch you can use
mView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
//show dialog here
return false;
}
});
if your YourActivity implements OnTouchListener you can get the event and where the user touched on the screen:
public class YourActivity extends Activity implements OnTouchListener {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
System.out.println("I touched: "+event.getX()+"-"+event.getY());
}
}
Actually I don't know what you exactly mean. Do you mean text input on an EditText or in General when something is touched?
If you mean text you can use TextWatcher
http://developer.android.com/reference/android/text/TextWatcher.html
If you mean on a View directly you can use OnTouchListener as mentioned above.
https://developer.android.com/reference/android/app/Activity.html#onUserInteraction()
Activity
public void onUserInteraction ()
Added in API level 3 Called whenever a key, touch, or trackball event
is dispatched to the activity. Implement this method if you wish to
know that the user has interacted with the device in some way while
your activity is running. This callback and onUserLeaveHint() are
intended to help activities manage status bar notifications
intelligently; specifically, for helping activities determine the
proper time to cancel a notfication.
All calls to your activity's onUserLeaveHint() callback will be
accompanied by calls to onUserInteraction(). This ensures that your
activity will be told of relevant user activity such as pulling down
the notification pane and touching an item there.
Note that this callback will be invoked for the touch down action that
begins a touch gesture, but may not be invoked for the touch-moved and
touch-up actions that follow.