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);
}
Related
I have a bottom drawer - a view that is bottom aligned with the bottom of the screen and when dismissed animates itself down until it becomes invisible (in such state its top border is aligned with the bottom of the screen). I am trying to implement dismissing this view by vertical swipe down on it. To do it I attached a gesture detector in this method of the view:
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
I did it this way since this view contains a horizontal recycler view, so I do not want to intercept the child touches too early to make sure my view responds only to swipe down and does not steal horizontal touches from the recycler view (as described here: https://developer.android.com/training/gestures/viewgroup.html)
Now, my problem is that when I swipe on one area of this view, the fling recognition works, while when I swipe on another area ofthis view it does not work. In the first case I see in the debugger that onInterceptTouchEvent is called three times, the third call is recognized by the gesture recognizer as fling. In the second case I see that onInterceptTouchEvent is called only once.
It looks that some other view is stealing my touches, but I have no idea which one is that since my app is pretty complex and I am not the only author :). What is an easy way of debugging that in a general case? I would like to just know what view in the entire hierarchy of my activity consumed my event making onInterceptTouchEvent called only once - that would be a good start for the further investigation.
Thanks!
The way I do it is using emulator with android source code downloaded.
I put a breakpoint in View.dispatchTouchEvent (all places that returns true). This will show me which view is handling the event.
If this does not help, you can also place various breakpoint log messages.
I hope this will help you solve the problem :D
It's my understanding that Android event propagation goes from parent to child, that is to say, it starts with the outermost element and inwards from there. My question is, why is it that when I try to scroll vertically a listview that is inside a viewpager that is wrapped on a scrollview, the listview moves, and not the viewpager.
Okay, let me rephrase that:
I'm trying to create a menu that appears when the user pulls down the view pager, let me make that even clearer:
Scrollview
My custom Menu
ViewPager (with three fragments, all of them have a lisview)
ListView
I understand that what I'm trying to do is a bit odd, but bear with me just for this time. :)
What can I do to "disable" momentarily the list views scrolling.
Thanks
It seems you have to override the scrolling event. A good webpage is Disable scrolling in Android ListView .
Mainly look at dispatchTouchEvent. Snippet of it:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_MOVE) {
// Ignore move events
return true;
}
Personally I wish it is simpler than this like disabling scroll method.
I tried to understand how Android handle touch event and got a little bit confused. From what I understand touch event are send to the root view and pass down to the children.
I have a FrameLayout that is a container for Fragment.
First fragment view is a ScrollView, second one is some kind of Gallery (HorizontalListView) and the last one is also FrameLayout. Only one fragment in the layout each time.
What I want to do is to identify user swipes on the screen, for the app use. I want to count the swipes and do something after some number of swipes.
I tried to put a OnTouchListener on the top FrameLayout but it doesn't get called when the child is the ScrollView or the Gallery. I tried to return false and also true in the end of onTouch, but I get same result - it's never being called.
How can I do it?
I just want to "transparently" handle the touch events and passing them on like I didn't even touch them.
My understanding is that it actually goes the other direction. The Child views get their event triggered first (sort of). The root view get's it's dispatchTouchEvent() called, which propagates the event down to the children's onTouchEvent(), and then, depending on whether they return true or false, the parent's onTouchEvent() is called.
The normal solution for intercepting things like this is to override dispatchTouchEvent(MotionEvent ev) in one's activity like so:
#Override
public boolean dispatchTouchEvent (MotionEvent ev) {
// Do your calcluations
return super.dispatchTouchEvent(ev);
}
The documentation for this one is here. Note that you can also override that method in any ViewGroup (such as a FrameLayout, etc)
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.
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.