2
votes

I have a problem using ScrollViews inside of ScrollViews in Android. I know it's not kind of best practice, but I need to get it working.

public static void disableScrollOnParentHorizontalScrollView(View v) {  
    v.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            int action = event.getAction();
            switch (action) {
            case MotionEvent.ACTION_DOWN:
                requestDisallowInterceptTouchEventByDirection(v, true, HorizontalScrollView.class);
                break;
            case MotionEvent.ACTION_UP:
                requestDisallowInterceptTouchEventByDirection(v, false, HorizontalScrollView.class);
                break;
            }
            v.onTouchEvent(event);
            return true;
        }
    });
}

I have the same method like above for vertical ScrollViews with ScrollView.class as a parameter.

private static void requestDisallowInterceptTouchEventByDirection(View v, boolean b, Class<?> clazz) {
    while (v.getParent() != null && v.getParent() instanceof View) {
        if (clazz.isInstance(v.getParent())) {
            v.getParent().requestDisallowInterceptTouchEvent(b);
        }
        v = (View) v.getParent();
    }
}

Here's a screenshot and I want to show you what exactly is the problem.

  • A nested HorizontalScrollView inside of a ScrollView should scroll both vertically (parent) and horizontally (own).
  • A nested ScrollView inside of a HorizontalScrollView should scroll both horizontally (parent) and vertically (own).
  • A view inside of inside of a HorizontalScrollView and a vertical ScrollView should scroll both parents.

This works in most cases.

screenshot

The TextViews 5 and 9 are nested in ScrollViews, TextViews 6 and 10 are nested in HorizontalScrollViews.

  • View #2 is scrolling vertically (2) and parent horizontally (1)
  • View #3 is scrolling horizontally (3) and parent vertically (2)
  • ...
  • View #6 is scrolling horizontally (6) and parent vertically (4)
  • View #7 is scrolling parent horizontally (3) and parent vertically (4)

So any view should scroll it's parent when it is not scrollable by itself.

My problem is that a vertical scrolling view (5) inside a vertical scrolling view (4) does not scoll the outer vertical view's horizontal scrolling parent (3).

Same problem in the other direction (View #10 (horizontal scrolling) does not scroll it's parent's (8, horizontal scrolling) parent (4, vertical scrolling), but the view right of view #10 scrolls both, 8 (horizontal) and 4 (vertical)).

Seems like there are wrong interceptions on some views, but I don't find the problem.

Can anyone help me?

Best regards

1

1 Answers

0
votes

Possible Solution

Okay, to complete this: I found a horrible solution, but it works.

If there are two nested HorizontalScrollViews, I search for the first (or most inner) vertical ScrollView and disable the interception.

public static void disableScrollOnParentHorizontalScrollView(View v) {

    v.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            int action = event.getAction();

            switch (action) {
            case MotionEvent.ACTION_DOWN:
                requestDisallowInterceptTouchEventByDirection(v, true, HorizontalScrollView.class); //funzt weitgehend
                if (isDirectlyHorizontalScrollViewAbove(v)) {
                    findFirstScrollViewAbove(v).requestDisallowInterceptTouchEvent(false);
                    findFirstScrollViewAbove(v).getParent().requestDisallowInterceptTouchEvent(true);
                }                   
                break;
            case MotionEvent.ACTION_UP:
                requestDisallowInterceptTouchEventByDirection(v, false, HorizontalScrollView.class);
                break;
            }
            v.onTouchEvent(event);
            return true;
        }
    });
}


private static boolean isDirectlyHorizontalScrollViewAbove(View v) {
    while (v.getParent() != null && v.getParent() instanceof View) {
        if (v.getParent() instanceof HorizontalScrollView) {
            return true;
        } else if (v.getParent() instanceof ScrollView) {
            return false;
        }
        v = (View) v.getParent();
    }
    return false;
}


private static ScrollView findFirstScrollViewAbove(View v) {
    while (v.getParent() != null && v.getParent() instanceof View) {
        if (v.getParent() instanceof ScrollView) {
            return (ScrollView) v.getParent();
        }
        v = (View) v.getParent();
    }
    return null;
}

// same method like before
private static void requestDisallowInterceptTouchEventByDirection(View v, boolean b, Class<?> clazz) {
    while (v.getParent() != null && v.getParent() instanceof View) {
        if (clazz.isInstance(v.getParent())) {
            v.getParent().requestDisallowInterceptTouchEvent(b);
        }
        v = (View) v.getParent();
    }
}

When someone has a better solution (or found the problem with my interceptions), please answer :)

Best regards