Scrollview doesn't swipe when it's too short to scroll - android

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.

Related

Android: How to figure out what view is stealing my touch events?

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

dispatchTouchEvent and onTouchEvent in Android

Helle there,
I've been working with the CoordinateLayout inside i have nestedScrollview layout and it's childs.
It's similar to whats'app profile view. I have tried to mimic this ontouch and dispatchtouch event for more than two days without success.
I read lot about dispatchTouch event onTouchEvent, setOntouchListener. and it's related video's.
I read about Managing ViewGroup reference in android reference, but still it's confuses me. How to handle those touch events.In nestedScrollView i have child linearlayout and this layout contains lot of child views like button and edittext.
When i touch nestedScrollview (ActionDown) triggered and subsequently (ActionUp) started to scroll, but when i touch any of it's child's view it should wait for the movement of my finger and then start to call nestedScrollview if its a (ActionMove).
I've dealing this inside of AppCompatActivity.
How do i achieve this? Please help.
Thanks.
Stupid enough to play with what i don't understand.
I was able to mimic like whatsapp profile,
#Override
public boolean dispatchTouchEvent(MotionEvent motionEvent) {
nestedScrollview.onTouchEvent(motionEvent);
return super.dispatchTouchEvent(motionEvent);
}
call actionMove on child view to consume the event. Voila this did that trick. How ever the Scroll was not smooth compared to the mimic :0
Call this child.setOnTouchListener
case MotionEvent.ACTION_MOVE:
if (zeroY < mTouchSlop) {
mIsScrolling = true;
Log.d(DEBUG_TAG, "general" + Float.toString(zeroY) + " " + Float.toString(plusY));
}
return true;

Android ListView, not all Touch-Events

I want to use a ListView (and have done this successfully before) containing custom Views.
Basically these custom views are vertical sliders, obviously conflicting with the natural behaviour of the ListView.
I fill it with my custom Views from an Adapter, and these react to the touches on the Items,
but once I move my finger more than a few pixels, it will scroll, and the custom View will not receive any touch-events anymore.
How can I (nicely) prevent the ListView from scrolling, when I touch my own components?
Can I somehow disable ListView Selection, and just forward the touches to the Items, but still use the scrolling behaviour?
Thank you in advance.
To prevent the scrolling of listview you can inplement on touch listener as follows
listView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_MOVE)
{
return true;
}
return false;
}
});
hope it will work and if it us useful to you give vote
You can intercept all touch events using onInterceptTouchEvent() in your root layout (one that contains the ListView, like a FrameLayout) as found here.
What you do there is capture the motion events (return true when a MotionEvent.ACTION_DOWN comes in), capture the following event in onTouchEvent(), decide whether the motion is meant for the list items or the list itself and accordingly dispatch the events.
Don't expect this to work easily. Understanding the flow of motion events and the interaction between onInterceptTouchEvent() and onTouchEvent() is challenging and making it work even more so. But I'm confident that this is a feasible way to solve your problem.

How to add touch event for a Layout that contains a ScrollView

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.

Way to check if user is scrolling?

I have a scrollview in my layout and I want to be able to check if the user is scrolling. I checked the Scrollview in Android Docs, but I don't see anything like isScrolling() that returns a boolean.
My question is: is there a way to check if the user is attempting to scroll, as opposed to clicking a button? Could there be some other class in the Android core that is monitoring the scroll event?
There could be a better way but here is how you can figure it out.
You get a MotionEvent object inside the onTouchEvent(MotionEvent ev) method.
check the action of the motion event as ev.getAction().
If the action is ACTION_MOVE it implies that a scrolling is taking place.
Note: Scrolling is different than a fling. So the same logic cannot be used to determine if the fling is over.

Categories

Resources