This probably will look quit strange but I want to use two nested scrollviews. At some point the outer scrollview needs to stop scrolling and pass all scrolling behaviour to the inner scrollview.
My layout looks something like this.
<CustomOuterScrollView>
<LinearLayout/>
<CustomInnerScrollView/>
</CustomOuterScrollView>
I made two classes where I override the onInterceptTouchEvent.
For the CustomOuterScrollView this is the following:
#Override
public boolean onInterceptTouchEvent( MotionEvent ev )
{
if ( stopScrolling )
{
return false;
} else
{
return super.onInterceptTouchEvent( ev );
}
}
And For the CustomInnerScrollView this look just like this:
#Override
public boolean onInterceptTouchEvent( MotionEvent ev )
{
return true;
}
Now, the biggest problem I have is the following. After the point where stopScrolling variable is set true the outerscrollview doesn't consume the events anymore but passes them to the CustomInnerScrollView. Which when I debug, actually comes in to the onInterceptTouchEvent. The only problem is that the InnerScrollView will not scroll. Anyone who has an idea? I know this is not normally done, using nested scrollviews. But due to design issues it's not possible to do something else.
Related
I have a recyclerview with items inside, I would like to distinguish between swipe (I am moving the element horizontaly making some action and setting it back to its original place) singleTouch and longClick, what is the best Practice of achieving it?
I seen a lot of implementations here, but none that work\super messy(and also dont work properly), if i implement custom gestureDetector and switchcase inside it works but it takes the phone about a second to react, if i implement onTouch in only catches onTouch, if I dont the onClick and onLong work but not the swipe
currently only the swipe works:
item.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
logd("TAG","2");
return false;
}
});
}
item.setOnClickListener(v -> {
logd("TAG","1");
});
item.setOnLongClickListener(v -> {
logd("TAG","2");
});
any advice would be appreciated,
Thanks
I have a ListView. Inside the cells, I have a custom view. (You can draw in it.)
When drawing, I turn off scrolling of the list ..
theListView.setOnTouchListener(new View.OnTouchListener()
{
public boolean onTouch(View v, MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_MOVE)
{
if ( STATE.weAreDrawoning )
{
return true;
// so, do not forward and hence do not scroll the list
}
else
{
return false;
}
}
return false;
}
});
That's fine. But strangely, up-down touching in the custom View, is still passed on to the list and makes it scroll.
public class AmazingCustomView extends View
{
blah
#Override
public boolean onTouchEvent(MotionEvent event)
{
blah
return true;
}
}
notice in the custom view onTouchEvent is returning true (I also tried false! :) )
but the motion events appear to be still passed on .. what gives??
Is there another "on .. something" I'm missing in the custom view? Sorry, new to Android and lame. Thanks!
PS, I tried turning on "clickable" on the xml of the custom view, didn't help :O
--
Worse ...
I've just realised ALL controls in the ListView, say buttons, still "pass on scrolling"
I fear the system I use above for turning off scrolling is just no good. :/
important...
For anyone googling to here. The only real way I've found to turn off scrolling on an android listView ...
danosipov.com/?p=604
(don't forget to separately turn off your pull-to-refresh)
You may need to override onInterceptTouchEvent. Its an odd function, documentation here: http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent%28android.view.MotionEvent%29
I have a container ViewGroup, lets call it screen inside a ScrollView. This container view hosts a number of other Views let's call them widgets, and some of them are interested in preventing the ScrollView from scrolling and using the MotionEvent theirselves (for example a pannable image)
I can't figure out the proper event intercept strategy to use. ScrollView always processes the event before the children, or the children process the event but scrollview is disabled.
I read about issuing getParent().requestDisableInterceptTouchEvent() in the child views if this view wants to capture the event, but their onTouchEvent is not called, I suppose because ScrollView has engulfed the event beforehand. I guess the fact that I have 2 levels of layers (container + widgets) prevents this from working, I suppose the container ViewGroup has to play an important part here, but I can't figure out which one...
Can I know, at the ScrollView's onInterceptTouchEvent level, which widget on the container viewGroup has been touched to decide if I should intercept or not?
or...
How can the 'widget' layers in the ViewGroup get the event before ScrollView so I can call getParent().onRequestDisableInterceptTouch() ... or is it getParent().getParent().onRequestDisableInterceptTouch()?
Thanks in advance
I've read related questions but no luck ...
Handle touch events in ScrollView Android
Well after a night of coca cola & debugging I managed to get this to work. I share the solution just in case it is of interest to anyone, because it took me quite a lot of time to get it running.
I didn't manage to get it running with getParent().onRequestDisableInterceptTouch(), I was close, but couldn't find a way for the child widgets to get the MotionEvents they need for scrolling once I intercepted the touch on the parent, so even though the outer scroll was prevented correctly, the inner widgets didn't scroll.
So the solution is to interceptTouchEvents in the children ONLY, and if the children is scrollable (known property), and the touch is ACTION_DOWN, then disable the scrollview two levels above. If the touch is ACTION_UP, we enable the scrollview.
To enable/disable the scrollview I just intercept the touch event and with a flag filter the event or not.
I did three auxiliary classes, one for the ScrollView, one for the Container, One for the widgets:
This class wraps every widget and, if I call setNeedsScroll(true) , then touches will be intercepted, and when it is touched, it will (tell the container to) tell the scrollview to disable itself. When the touch is released, it will re-enable the scrollview.
class WidgetWrapperLayout extends FrameLayout {
private boolean mNeedsScroll=false;
public WidgetWrapperLayout(Context context) {
super(context);
}
/** Called anytime, ie, during construction, to indicate that this
* widget uses vertical scroll, so we need to disable its container scroll
*/
public void setNeedsScroll(boolean needsScroll) {
mNeedsScroll=needsScroll;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mNeedsScroll) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
((SlideLayout)getParent()).setEnableScroll(false);
break;
case MotionEvent.ACTION_UP:
((SlideLayout)getParent()).setEnableScroll(true);
break;
}
return false;
}
return super.onInterceptTouchEvent(ev);
}
}
This is the container, only child of the scrollview, and holds the different widgets. It just provides methods for the children so they can enable/disable the scroll:
public class ContainerLayout extends FrameLayout {
public ContainerLayout(Context context) {
super(context);
}
public void setEnableScroll(boolean status) {
if (Conf.LOG_ON) Log.d(TAG, "Request enable scroll: "+status);
((StoppableScrollView)getParent()).setScrollEnabled(status);
}
}
and finally a scrollview capable of deactivation. It disables the scroll 'old-skool', intercepting and blocking events.
public class StoppableScrollView extends ScrollView {
private String TAG="StoppableScrollView";
private boolean mDisableScrolling=false;
public StoppableScrollView(Context context) {
super(context);
}
/** Enables or disables ScrollView scroll */
public void setScrollEnabled (boolean status) {
if (Conf.LOG_ON) Log.d(TAG, "Scroll Enabled "+status);
mDisableScrolling=!status;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mDisableScrolling) return false;
return super.onInterceptTouchEvent(ev);
}
}
Implement View.OnTouchListener in your Activity and add that listener to the ScrollView. Then return true in onTouchEvent(...). Before returning call the onTouch of the children you want to handle that event.
I have a application that need event handling on a unusual way.
For my question, let me first explain a simple case that the current event handling system of Android don't fits for me.
Supposing that I have a FrameLayout (that I'll call ViewSwiper since now) that all Views added on it are MATCH_PARENT X MATCH_PARENT (that I'll call PageView), it's handles events by translating the actual View and replace it based on the direction of moving.
This component I already have done and work properly ( Using Animation to swipe views ).
But the problem is on that PageView I add on top of it, in case of ImageViews that return false on it's onTouchEvent, the ViewSwiper will handle the events and let another PageView enter the screen, but if I add a ScrollView on that, all the events will be consumed by the Scroll and the ViewSwiper will not have chance to replace the PageView.
So I figured out that returning false onTouchEvent of the ScrollView the parent can assume it's events, I wrote this sub-class of ScrollView to test it:
public class ScrollViewVertical extends ScrollView {
public ScrollViewVertical(Context context) {
super(context);
setOverScrollMode(OVER_SCROLL_ALWAYS);
setVerticalScrollBarEnabled(false);
}
public boolean onTouchEvent(MotionEvent evt) {
super.onTouchEvent(evt);
return false;
}
}
But returning false make any further events to get dispatched to the parent, but I need these events for VERTICAL scrolling, so I have the idea to return falses only if the user are moving HORIZONTAL, that's what my code looks like:
public class ScrollViewVertical extends ScrollView {
private MovementTracker moveTracker;
public ScrollViewVertical(Context context) {
super(context);
setOverScrollMode(OVER_SCROLL_ALWAYS);
setVerticalScrollBarEnabled(false);
moveTracker = new MovementTracker();
}
public boolean onTouchEvent(MotionEvent evt) {
if (moveTracker.track(evt))
if (moveTracker.getDirection() == Direction.HORIZONTAL)
return false;
return super.onTouchEvent(evt);
}
}
PS: MovementTracker will returns true on track() after some events and tell on which direction the user is moving.
But in that case, the ScrollView keep receiving events since it's returns true on the first events.
Any ideas on how can I handle the events on ViewSwiper when it's child returns false (even if some trues are returned).
PS: I can give more info about this if needed, and accept different solutions also.
Based on answers I tried the following:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
onTouchEvent(ev);
return intercept;
}
public boolean onTouchEvent(MotionEvent evt) {
boolean x = super.onTouchEvent(evt);
if (moveTracker.track(evt)) {
intercept = moveTracker.getDirection() != Direction.VERTICAL;
if (!intercept)
getParent().requestDisallowInterceptTouchEvent(false);
}
return x;
}
Still nothing.
try this in onTouchEvent() of the scrollview
//if (evt.getAction() == MotionEvent.ACTION_MOVE) {
if (moveTracker.track(evt)){
if (moveTracker.getDirection() == Direction.VERTICAL){
//Or the direction you want the scrollview keep moving
getParent().requestDisallowInterceptTouchEvent(true);
}
}
return true;
Update
Please try the following to the custom Scrollview
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
super.dispatchTouchEvent(ev);
return false;
}
And nothing else
This way i assume the MotionEvent will perform on both views. And since they don't conflict (One is vertical the other one is Horizontal) this could work
Based on the answer from weakwire, I came to the following solution:
On ViewSwiper
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!super.dispatchTouchEvent(ev))
onTouchEvent(ev);
return true;
}
And on ScrollHorizontal I return false on dispatchTouchEvent when I don't need then anymore.
I have a two ImageViews.
1.ImageView zoomImageMoveable : should zoom in,out and moveable.
2.ImageView zoomImageFixed : show zoom this is guide line.
I want run different functions from one touch.
{
FrameLayout myFrame = new FrameLayout(this.getContext());
myFrame.addView(zoomImageMoveable);
myFrame.addView(zoomImageFixed);
}
Case 1. I tried to attach a listener to each of the view, as they are overlapping only one works.
Case 2. I also tried to wrap the two view with a FrameLayout and added an OnTouchListener to that layout to have it forward the calls to each of the assigned methods. Yet this method also didn't work.
myFrame.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
zoomImageMoveable.onTouchEvent(event);
zoomImageFixed.onTouchEvent(event);
return false;
}
});
How should I approach this issue?