3

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.

Community
  • 1
  • 1
Marcos Vasconcelos
  • 18,136
  • 30
  • 106
  • 167

2 Answers2

3

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

weakwire
  • 9,284
  • 8
  • 53
  • 78
  • This does nothing, one question, for that case I should implement onInterceptTouchEvent as well? And if that's the case, how i'll know if the child requested it to intercept? – Marcos Vasconcelos Sep 06 '11 at 19:18
  • Try returning always true.The technic here is that you want by default all the touchevents to go to the parent.getParent().requestDisallowInterceptTouchEvent(true) on a child interrupts and takes control. This should work if all the events(even on the scrollview) are controlled by the parent view until the child intercepts and not the opposite. – weakwire Sep 06 '11 at 19:48
  • So, how I'll make the parent handle all events and make the child intercept? – Marcos Vasconcelos Sep 06 '11 at 19:50
  • that's the idea yes. Handle all MOVE event's. Not others. That way only the child can intercept – weakwire Sep 06 '11 at 20:05
  • To do that override dispatchTouchEvent() in your childs and don't call super.dispatchTouchEvent(). Via http://groups.google.com/group/android-developers/browse_thread/thread/d15edc816a0b436c – weakwire Sep 06 '11 at 20:09
  • I tried this solution, the events stop coming to the ScrollView but the ViewSwiper onTouch was not called. – Marcos Vasconcelos Sep 06 '11 at 20:58
  • Still nothing, I did a lot of attemps with this method, but still nothing, if the event stop being handled on this ScrollView the parent still don't assume its. – Marcos Vasconcelos Sep 08 '11 at 14:04
1

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.

Marcos Vasconcelos
  • 18,136
  • 30
  • 106
  • 167