I have a ScrollView -> Table => TableRow that I dynamically add Rows to, each row has a LinearLayout inside of it that I attach a OnTouchListener to and then when its touched I do something. At least that was the plan, the problem I am having is that when you scroll in the ScrollView, even while scrolling it fires off these events. This type of behavior does not occur for the other controls I have in the ScrollView such as Buttons, ImageButtons, EditText
My question is how do I get the LinearLayout to ignore these OnTouch events while the ScrollView is scrolling like the Button and EditText fields do?
Inside your onTouch event callback add an if statement or case block to that that checks for
(me.getAction == MotionEvent.ACTION_CANCEL) similar to this:
else if (me.getAction() == MotionEvent.ACTION_CANCEL){
Log.i(myTag, "Action Cancel");
//This means we are scrolling on the list, not trying to press
}
It has been a long time since I worked on it, but I know I had to solve this problem at one point, and upon a quick glance just now I think this is what I had to do in order to get it working. It is going to keep receiving callbacks while the list is scrolling, but the action on them should be cancel. So if you set up some sort of if, or switch/case that checks for action_cancel and does nothing when it is true, from the users perspective the onTouch will "ignore" the events that happen while scrolling.
Related
I have ListView with complex items, that contain a few buttons and checkboxes (all of those buttons and checkboxes have their onClickListener's). Every item in listView might be deleted by dragging it to the left/right side. To make items draggable I override onTouchListener for ListView.
My problem is that when I try to drag an item or to scroll the ListView up/down, I don't receive ACTION_DOWN event in onTouch() method, but only when I start the action from the area of any of those Checkbox'es and Button's. So it looks like those elements steal my event. What I want to do, is to receive ACTION_DOWN, ACTION_MOVE, ACTION_UP events in onTouchListener of ListView, and then when ACTION_UP occurs check out whether I'm scrolling/dragging (which I have no problem with), and if I'm not, perform click either for some Button/Checkbox (if ACTION_DOWN started from its area) or for an item (if ACTION_DOWN started from some area that doesn't belong to any of buttons/checkboxes).
Please check this SwipeMenuLib library. It may be useful for you
I have a layout with a TextView in a ScrollView because scrolling in the TextView doesn't work well and I have a custom ActionMode in addition to the default txt ActionMode and scrolling in the textview directly for some reason destroys my ActionMode.
However, my Problem:
In my app you can make selection in the text and edit those selections later, I implemented these selections myself since the 'normal' selection mode doesn't work (no handles on selections that were set programatically...!?) When I edit a selection it works as long as the text is not scrolled (i.e. the top line is on top of the scrollview). When the text is scrolled, the selection works as long as I stay in the same line. Once I go up or down, the TextView's onTouch gets a 'cancel' event and the OnTouchListener of the ScrollView is called...
1) Why does the TextView's MotionEvent get cancelled?
2) Is there a way I can prevent it? Maybe tell the ScrollView to ignore everything for the time I'm editing the selection?
3) Is there a way I can map the coordinates that I get in the ScrollView's onTouch to coordinates in the TextView?? (So I could at least dispatch the event to the txt view and 'pretend' as the 'cancel' never happened....
Any ideas???
Thanks
Ok I found a solution.
I implemented my own ScrollView where I overwrite onInterceptTouchEvent and return false while the textView is being edited...
I have a ViewPager that contains two fragments. In any of the fragments I can touch a place, swipe to switch to another fragment. One of the fragments contains a list. Items in the list contains one TextView and one ImageView. The issue is, if you dragging has been started from tapping the ImageView, it's OK. But if it's been from the TextView, the drag was never known to the ViewPager, as a result the 'smooth switching' never happens.
Any clue on this?
EDIT
This picture is to show how my GUI is. If the drag has been started from TextViewE, it doesn't begin.
This thing bothered me too, but I've managed to find the answer.
Basically, the case is: if the view can scroll horizontally, it intercepts the horizontal motion event and ViewPager is not able to process it anymore.
Since API Level 14 TextViews have android:scrollHorizontally property (and setHorizontallyScrolling(boolean) method), which, if set to true, causes the TextView to intercept horizontal scroll motion events.
You may set it to false either in XML or right in the code, but watch out: android:singleLine property forces android:scrollHorizontally to be set to true! Very tricky point! But fortunately, you usually able to safely replace single line property with android:maxLines="1" and necessary ellipsize value.
Good luck!
you can override onTouchEvent() of the TextView:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
return false;
}
return super.onTouchEvent(event);
}
The problem is the the scrolling will intercept with touch event has set to the parent layout.
Can I keep the onTouch event with the scroll in ScrollView ?
This is a very tricky part. There is an overriden method from Activity which is: public boolean onTouchEvent(MotionEvent event)
This is the general method that interprets all the touch events from the whole screen. And you could say, "ok, I can implement this and I am good to go..". And here comes the difficult part on how android works.
As you know every View has its own onTouchEvent() method that you could implement in order to add some custom implementation. So which method will listen? The ScrollView or the Activity? It appears that these touch events go from the "inside" elements to the "outside" elements. I mean parent-child relations.
Another thing to take into account is that the onTouchEvent method returns a boolean. This boolean parameter determines whether the touch event should go one level up or it is handled by the current View. Meaning that if you have a CustomViewA that implements the onTouchEvent() and CustomViewB implementing its own touch event, and the A is a child in B then the touch event would go through A first and if it is not handled it would go to B.
So basically yes it could be done. It depends on what touch event you wanted to do.
So in our case, the ScrollView returns true when the touch events are a horizontal. The activity's touch event will be handled only if the ScrollView touch event is not handled by itself then you are fine. Otherwise you have to override and implement the on touch event of scroll view and in some cases you have to return false so as for the whole layout to implement it. Good luck with the last part. I started to implement a fling effect but came up with some difficulties so I have implemented a 2 finger move with scroll view in it and it works like a charm.
This is about a week of research and experimenting and it is an overview of what I came up with. if you find anything else please let me know. Hope it helped.
I'm pretty new to Android app development, and I've been playing around with swipe gestures using Android's SimpleOnGestureListener and a ViewFlipper. There are 3 children of the ViewFlipper, and each is a ScrollView. They're all dynamically populated when the Activity loads, and they don't change after that. The ScrollView is where the SimpleOnGestureListeners are attached.
Here's the layout I'm using:
+ViewFlipper
++ScrollView (x3, one for each page, each with the following:)
+++LinearLayout (vertical)
++++TextView
++++TableLayout (dynamically populated w/TableRows)
++++View
I extended the onFling method with the common tutorial code you can find anywhere online, and it works great--except when one of the ScrollViews doesn't contain enough content to scroll.
I've narrowed the problem down to touch detection by overriding and calling super on every one of the SimpleOnGestureListener's methods to add a print-to-log.
When I swipe on a page that scrolls, I get something full of "in onClick" "in onScroll" "in onFling" etc. On a page that's too short to scroll, I get "in onClick" "in onShowPress" "in onLongPress", and that's only if I'm touching the content within the too-short scrollview's children--if I touch elsewhere I get no events at all.
Ideas on what's wrong, or how to detect the swipe gesture no matter how big the ScrollView is?
EDIT: I've determined that when I run this on an Android 2.2 emulator, as opposed to the Android 2.1u1 DroidX emulator I've been using, it goes away. This is reproducible across multiple environments.
I have some more insight on this; it seems as though onInterceptTouchEvent is not called for every motion event when a scrollview is contained within a flipper (or a WorkspaceView).
In particular, the behavior I found while modifying another view class to fix this very same issue (it is not unique to flippers) was as follows--note that this is Android 2.1 only:
If the scrollview is long enough to scroll, the ACTION_DOWN motion event is caught by the ScrollView, and every subsequent ACTION_MOVE event goes through onInterceptTouchEvent of the flipper, where it is intercepted and handled appropriately. In android 2.2, this behavior happens regardless of the scroll length.
Back to 2.1: If the scrollview is not long enough to scroll, the ACTION_DOWN motion event is not caught by the scrollview, but instead comes back to the onTouchEvent of the flipper. All subsequent ACTION_MOVE events of the same gesture skip the onInterceptTouchEvent function and go straight to the onTouchEvent function!
The way I resolved this was to take the functionality I had in onTouchEvent for ACTION_MOVE events and refactor it into its own method. In this way, I can have onTouchEvent call onInterceptTouchEvent followed by that functionality if it detects that the event has previously gone unhandled.
case MotionEvent.ACTION_MOVE:
if (touchState == TOUCH_STATE_SCROLLING) {
handleScrollMove(ev);
} else {
// Log.d("workspace","caught a move touch event but not scrolling");
//NOTE: We will never hit this case in Android 2.2. This is to fix a 2.1 bug.
//We need to do the work of interceptTouchEvent here because we don't intercept the move
//on children who don't scroll.
Log.d("workspace","handling move from onTouch");
if(onInterceptTouchEvent(ev) && touchState == TOUCH_STATE_SCROLLING){
handleScrollMove(ev);
}
}
break;
This is from WorkspaceView.java (a modification of Android's Workspace.java, found at the andro-views project on google code, and now here: Horizontal "tab"ish scroll between views ). In the case that we receive a move event, and we are scrolling (which only happens if we have deliberately chosen to intercept it--ie, it's set in the intercept function, so we've been to the intercept function already) we perform the move behavior we desire. If we receive a move event here and we are not scrolling, then we send the event back through onIntercept, and then see if we're now set to scrolling. If so, we perform the action.
It's not elegant, but it works!
I needed to create a new class that extended ScrollView, and used this:
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
return gestureDetector.onTouchEvent(event);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev){
gestureDetector.onTouchEvent(ev);
super.dispatchTouchEvent(ev);
return true;
}
I have no idea why, but if I try to return anything but true in dispatchTouchEvent (the logical thing would have been to
return (gestureDetector.onTouchEvent(ev) || super.dispatchTouchEvent(ev));
if I understand properly), it doesn't work, and this does.
Try setting android:fillViewport="true" in your layout xml for each of the ScrollViews. That tells the ScrollView to be as large as the view it's contained in.
Had the same issue. You need to intercept the touch event on the children of the ScrollView when it's too short to have a scrollbar.