I know this got asked often, but all solutions(, I found,) don't work for me.
What I have is a CardView with an OnClickListener making a Toast (#toast1).
Inside the CardView there are multiple views as also a WebView.
As mentioned elsewhere, to pass through the click through the WebView to the CardView I have done following:
Set android:clickable="false" in WebView XML
insert following under CardView.setOnClickListener(...)
WebView.setOnTouchListener( (view, event) -> true);
I also replaced the lambda with an anonymous method, to see if its just this. No change.
What happens now is:
At the border and over the other views, the clickListener is triggered and the toast appears
Over the webView the clickListener isn't triggered.
Also put a toast (#toast2) in touchLstener of WebView before returning true, and it gets triggered.
What I expect:
Click will passed through WebView
With #toast2 added: First show #toast2 then #toast1
What is a bit confusing, that in documentation of OnTouchListener, the return is following:
True if the listener has consumed the event, false otherwise.
For me that means:
true: Don't pass click to below views, as listener consumed it
false: Pass click to below views, as listener didn't consumed it
But setting to false didn't change anything.
First of all I would suggest you to get familiar with android touch handling system - you can find a really good description in this answer. To sum it up: touch event propagation starts on top level of hierarchy, but actual handling of the touch event starts on the lowest level of view hierarchy. As for solution of your problem I may suggest to sublcass the parent of your WebView and override onInterceptTouchEvent in the following way:
#Override
public boolean onInterceptTouchEvent(MotionEvent e) {
return true;
}
This will instruct this parent view to intercept all touch events that would otherwise go to its children views, thus limiting the first level of touch processing to this view.
Related
I've got a big headache and spent a few hours to solve my problem withou any reasonable results.
I use a custom adapter (extending ArrayAdapter) to show listview items. This adapter implements OnTouchListener. There's also a background selector which color is changing when an item is touched and OnItemClickListener in ListViewActivity.
What I need is capture touch events (all of them, not only ACTION_DOWN) in the adapter. When onTouch returns false, the consecutive events (ACTION_CANCEL, ACTION_UP etc.) aren't captured. On the other hand returning true stops dispatching all other events (e.g. click) so that the ListViewActivity's onItemClick is never triggered.
I tried hard to find any working solution in SO and other resources, but with no success.
One idea was not worry about click events and background. I may set the background programatically and trigger click event the same way when the onTouch action is ACTION_UP, but view.performClick() does nothing (view is the 1st argument of onTouch method).
[EDIT]
To make it clear, I want to handle touch events in the adapter beacuse I need the textview in the item to marquee when the user touches it. Thus, in onTouch, when the action equals ACTION_DOWN, I assign true to setSelected property of the textview and, consequently, false when ACTION_CANCEL or ACTION_UP.
Any suggestions?
Thanks in advance.
This doesn't seem like a typical view layout which I'm having trouble with.
I have two listViews.
Each listView has touchListener
(whose purpose is to synchronize scrolling by calling dispatchTouchEvent() to another listView)
ListView also has onItemClickListener to handle clicks on the row of listView.
Everything works as intended up to here.
I'd like to add another clickListener to subview-group of the listView's row to handle click event on the subview.
After attaching this clickListener, I see listView's scroll doesn't always work.
I suspect its because the clickListener of this child view is inspecting touch events (to see if its indeed a click) before the parent(listView)'s touchListener.
I can think of two workarounds to this problem.
attach touchListener instead of clickListener to child, and make it return false for all touch event except FINGER_UP event.
on FINGER_UP event, I execute the method which I initially had in onClickListener
public boolean onInterceptTouchEvent (MotionEvent ev)
Implement this method to intercept all touch screen motion events. This allows you to watch events as they are dispatched to your children, and take ownership of the current gesture at any point.
(ok... I'm confused, I thought touch events goes to child views first and propagate to parents if children don't handle the touches..)
.. How do I implement the method 1?
.. Please help me to figure out #2 as well and to grasp the touch delivery mechanism.
EDIT -
This is where I add OnClickListener to my subview.
viewHolder.layout_author.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent profileIntent = new Intent(ImageListAdapter.this.activity, ProfileActivity.class);
profileIntent.putExtra("JsonUser", jsonAlbumImage.jsonUser);
ImageListAdapter.this.activity.startActivity(profileIntent);
}
});
I'd like to add another clickListener to subview-group of the listView's row to handle click event on the subview.
You simply need to write a custom adapter and assign the OnClickListener in getView(). You can look at the question: Android GridView Button Click Handler for example code.
Also awhile back I answered Android. Scrolling 2 listviews together did you use a similar approach to synchronize your ListViews? When I combine both of the answers, my app functions the way you want. I hope this helps.
in iOS world, there's a concept of touchUpInside, touch started within a view and user lifted a finger within a view bound.
I see touch vs click (event listeners) in android, and I'd like to know the difference between them.
Touch events are much more basic. There's many varieties. The most common being:
DOWN: The user touch the item
MOVE: The user shifts around the item after touching it
UP: The user stops touch the item
CANCEL: The user shifts off the item but is still touching the screen.
In Android, a click event is a composite of touch events. In the case of click it's both DOWN and UP action. There are others that can be found in a GestureDetector. Fling for example is a combination of DOWN, MOVE, and UP in a fast motion that signifies the user swiped the finger really fast.
EDIT:
Clarification on how Android handles the true and false in onTouchEvent().
It's not very well documented, but the way the Views work in Android is that when you touch a View, it will send that touch event from the parent view to all children views. Each child will then send the event to it's children. This is done in the dispatchTouchEvent() method.
When a parent View receives false from the child's onTouchEvent() method, it will no longer send touch events to that child. Meaning that when you're view returns false in onTouchEvent(), your View is really says:
I am done processing touch events and all of my children are done as well.
90% of the time, in onTouchEvent() you would do return super.onTouchEvent() to return the touch values of all the children in the View.
So let's look at your click example. Here's one way to do it in which you return false on a DOWN event, but true on an UP.
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return false;
case MotionEvent.ACTION_UP:
return true;
break;
default:
return false;
}
}
In this case, the parent of this View will receive false immediately. After which, it will stop sending touch events to this View because you said it was done.
Here's another way:
boolean mPossibleClick = false;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPossibleClick = true;
break;
case MotionEvent.ACTION_UP:
if(mPossibleClick) {
// UP event immediately after DOWN. Perform click action
}
default:
mPossibleClick = false;
}
return mPossibleClick;
}
It's rather crude, but basically here's what will happen. When the user first touches it, you will receive DOWN which will return true. If the user lifts the finger, it will perform a click action after which will return false because the event is done. If the user moves his finger or any other action is performed, it will return false and the click will be nulled out.
That last one is not the best way to implement a click, but I think it illustrates what I'm trying to say. In real life, move events are extremely common if even for a couple pixels.
onClickListener is used whenever a click event for any view is raised, say for example: click event for Button, ImageButton.
onTouchListener is used whenever you want to implement Touch kind of functionality, say for example if you want to get co-ordinates of screen where you touch exactly.
Definitions:
onClickListner: Interface definition for a callback to be invoked when a view is clicked.
onTouchListener: Interface definition for a callback to be invoked when a touch event is dispatched to this view. The callback will be invoked before the touch event is given to the view.
Details:
onClickListener: http://developer.android.com/reference/android/view/View.OnClickListener.html
onTouchListener: http://developer.android.com/reference/android/view/View.OnTouchListener.html
refer to the response of PH7
which one is better to use?
It really depends on your requirement.
onTouch gives you Motion Event. Thus, you can do a lot of fancy things as it help you separate state of movement. Just to name a few
ACTION_UP
ACTION_DOWN
ACTION_MOVE
Those are common actions we usually implement to get desire result such as dragging view on screen.
On the other hand, onClick doesn't give you much except which view user interacts. onClick is a complete event comprising of focusing,pressing and releasing. So, you have little control over it. One side up is it is very simple to implement.
do we need to implement both?
It is not necessary unless you want to mess up with your user. If you just want simple click event, go for onClick. If you want more than click, go for onTouch. Doing both will complicate the process.
From User point of view, it is unnoticeable if you implement onTouch carefully to look like onClick.
for more details : refer this and this
In a ListFragement I want to display additional edit options next to an item after the user long-clicks onto it.
This works fine using the following code in a custom ArrayAdapter (inside getView):
rowView.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View v) {
//Show the edit controls
View editArea = rowView.findViewById(R.id.editArea);
editArea.setVisibility(View.VISIBLE);
//Don't raise additional events for this touch
return true;
}
});
However, my problem is which event to use in order to hide those additional edit controls.
Ideally, when the edit controls are displayed, any touch outside them should be ignored in favor of hiding them (a little bit like when a dialog is opened and the user clicks somewhere besides it).
I tried overwriting dispatchTouchEvent in my MainActivity - if I generally intercept all ACTION_DOWN events, however, my edit controls are hidden before the click is dispatched to them (of course).
Is there any possibility to detect which control the user has touched without already dispatching the event?
If I intercept all ACTION_UPs, the edit-controls are removed when the user ends their long-click. And I am trying to avoid whether a specific ACTION_UP belongs to the long touch as this would produce some really messy code.
Any ideas which event to listen to?
Or do you know any better, alternate way to achieve my goal?
I a view have for which I would like to show a ContextMenu on a longPress. I was able to get this ContextMenu to display using the recommended method of: calling activity.registerForContextmenu and overriding onCreateContextMenu(...).
However, I would like to do other things on other touch events, so my view has a TouchHandler assigned to it. When this touch handler is set, the onCreateContextMenu() never gets called (presumably because my TouchHandler is eating the longPress). So, is there anyway for me to instantiate and show a ContextMenu without the onCreateContenxtMenu() method being called?
Alternatively, I could just show my own custom dialog with my "menu" items. Is there any disadvantage to using a custom dialog instead of the ContextMenu?
One thing to try, is to return false from your OnTouchListener if you don't want the event to be consumed.
What do you return from OnTouchListener.OnTouch? Returning false means you haven't consumed the event, which should mean that other actions can be peformed on it as well.