2

I have a layout that has a ViewPager inside of a custom ScrollView and the ViewPager won't scroll vertically. The custom ScrollView is used to fix the dreaded tab swiping with a ScrollView.

Yes, there is enough content for it to scroll and I've tried with and without the bottom buttons.

I'm using the ActionBar that has 3 tabs and the current code allows swiping left / right just fine.

My goal is to have the bottom bar for a couple action items such as save and cancel and the part between the ActionBar and the buttons at the bottom be the ViewPager that will scroll vertically.

I've tried too many combinations and am now sending this love letter to you. Any ideas? Any help is appreciated and feel free to ask for additional details if needed.

main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/footer"
        style="@android:style/Holo.ButtonBar"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/saveButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Save" />

        <Button
            android:id="@+id/cancelButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Cancel" />
    </LinearLayout>

    <com.blah.hootiehoo.ViewPagerCompatScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_above="@id/footer"
        android:fillViewport="true" >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical"
            android:scrollbarAlwaysDrawVerticalTrack="true"
            android:scrollbars="vertical" >

            <android.support.v4.view.ViewPager
                android:id="@+id/pager"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:scrollbarAlwaysDrawVerticalTrack="true"
                android:scrollbars="vertical" >
            </android.support.v4.view.ViewPager>
        </LinearLayout>
    </com.blah.hootiehoo.ViewPagerCompatScrollView>

</RelativeLayout>

MainActivity.java

ViewPager mViewPager;
TabsAdapter mTabsAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);
    mViewPager = (ViewPager)findViewById(R.id.pager);

    // setup action bar for tabs
    ActionBar actionBar = getActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    actionBar.setDisplayShowTitleEnabled(true);
    actionBar.setDisplayHomeAsUpEnabled(true);   

    mTabsAdapter = new TabsAdapter(this, mViewPager);

    Tab tab = actionBar.newTab();
    // tab setup
    mTabsAdapter.addTab(/*** stuff ***/);

    // add 2 more tabs, etc
}

ViewPagerCompatScrollView.java Got from here, at bottom

public class ViewPagerCompatScrollView extends ScrollView {
    private float xDistance, yDistance, lastX, lastY;

    public ViewPagerCompatScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                xDistance = yDistance = 0f;
                lastX = ev.getX();
                lastY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                final float curX = ev.getX();
                final float curY = ev.getY();
                xDistance += Math.abs(curX - lastX);
                yDistance += Math.abs(curY - lastY);
                lastX = curX;
                lastY = curY;
                if(xDistance > yDistance)
                    return false;
        }

        return super.onInterceptTouchEvent(ev);
    }
}
Community
  • 1
  • 1
Kirk
  • 16,182
  • 20
  • 80
  • 112
  • 2
    Is it an option to swap the `ScrollView` and `ViewPager` around? From experience I know that a `ScrollView` *inside* a `ViewPager` works without any problems. So basically instead of wrapping the `ViewPager` by a `ScrollView`, give every 'page' a `ScrollView` as root. – MH. Jun 02 '12 at 20:14
  • Worked perfectly once I understood what to do. This also eliminated the need for the custom `ScrollView`. Seems that this is quite the secret. If you want credit for it, please put an answer in shortly. – Kirk Jun 03 '12 at 06:03
  • Great, glad to hear to solved your problem. Took the liberty of adding the comment as answer below. Cheers. – MH. Jun 03 '12 at 07:30

3 Answers3

8

As per earlier comment:

Is it an option to swap the ScrollView and ViewPager around? From experience I know that a ScrollView inside a ViewPager works without any problems. So basically instead of wrapping the ViewPager by a ScrollView, give every 'page' a ScrollView as root.

Glad to hear that worked for you.

MH.
  • 45,303
  • 10
  • 103
  • 116
  • I've implemented my app exactly as you've suggested. Unfortunately the vertical ScrollViews inside the ViewPager will not do a smooth scroll (i.e. via the smoothScrollTo method). They simply "jump" to the final y position so the user has no appreciation that a scroll took place. Any suggestions? Thanks! – PeteH May 12 '14 at 06:24
4

MH. answer should work.

The problem in your code is that the ViewPager will have the same height as the ScrollView so it won't scroll. Each child View within the ViewPager will also get the same height as the ViewPager.

Try setting the height of the ViewPager manually eks android:layout_height="900dp" and it will scroll just fine...

But you should really use MH.´s answer :)

Nick Cox
  • 35,529
  • 6
  • 31
  • 47
Zelleriation
  • 2,834
  • 1
  • 23
  • 23
2

I had samillar problem. Here is how i solved it.

imageGrid.setOnTouchListener(new View.OnTouchListener() {

        int dragthreshold = 30;
        int downX;
        int downY;

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getRawX();
                downY = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                int distanceX = Math.abs((int) event.getRawX() - downX);
                int distanceY = Math.abs((int) event.getRawY() - downY);

                if (distanceY > distanceX && distanceY > dragthreshold) {
                    imageGrid.getParent().requestDisallowInterceptTouchEvent(false);
                    parentScrollView.getParent().requestDisallowInterceptTouchEvent(true);
                } else if (distanceX > distanceY && distanceX > dragthreshold) {
                    imageGrid.getParent().requestDisallowInterceptTouchEvent(true);
                    parentScrollView.getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP:
                parentScrollView.getParent().requestDisallowInterceptTouchEvent(false);
                imageGrid.getParent().requestDisallowInterceptTouchEvent(false);
                break;
            }
            return false;
        }
    });

Worth a try.

rawcoder064
  • 1,374
  • 3
  • 11
  • 26