56

I'm trying to use a ViewPager inside of a ScrollView, but the ViewPager does not appear. If I remove the ScrollView the ViewPager appears fine.

I've created a simple test project with the following:

main.xml layout:

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

        <android.support.v4.view.ViewPager
            android:layout_width="fill_parent" 
            android:layout_height="fill_parent"
            android:id="@+id/viewpager" />

    </ScrollView>
</LinearLayout>

Activity class:

public class ScrollViewWithViewPagerActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ViewPager vp = (ViewPager) findViewById(R.id.viewpager);
        vp.setAdapter(new MyPagerAdapter(this));
    }
}

class MyPagerAdapter extends PagerAdapter {

    private Context ctx;

    public MyPagerAdapter(Context context) {
        ctx = context;
    }

    @Override
    public int getCount() {
        return 2;
    }

    @Override
    public Object instantiateItem(View collection, int position) {

        TextView tv =  new TextView(ctx);
        tv.setTextSize(50);
        tv.setTextColor(Color.WHITE);
        tv.setText("SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, " +
                "SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, " +
                "SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, " +
                "SMILE DUDE, SMILE DUDE, SMILE DUDE");

        ((ViewPager) collection).addView(tv);

        return tv;

    }

    @Override
    public void destroyItem(View collection, int position, Object view) {
         ((ViewPager) collection).removeView((View) view);
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Parcelable saveState() {
        return null;
    }

    @Override
    public void restoreState(Parcelable arg0, ClassLoader arg1) {
    }

    @Override
    public void startUpdate(View arg0) {
    }

    @Override
    public void finishUpdate(View arg0) {
    }
}

Thanks for your time.

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
C0deAttack
  • 24,419
  • 18
  • 73
  • 81

4 Answers4

125

I've figured it out;

I needed to add android:fillViewport="true" to the ScrollView element.

C0deAttack
  • 24,419
  • 18
  • 73
  • 81
  • 25
    Hey COdeAttack, thanks for your answer. I have issues with the vertical scrolling? It does not scroll at all. Have you experienced that behavour? – gwvatieri Jan 04 '12 at 19:31
  • i am not able to solve the issue by doing so. can u please share the full xml – Harsha M V Dec 11 '12 at 19:31
  • Explain the reason @COdeAttack? – Diffy Jul 24 '14 at 08:57
  • This didn't work for me, because you can't scroll anymore... What is the configuration and the Layout XML used?? Also should not the scroll usually go inside each fragment used by the ViewPager? – Davideas May 25 '15 at 10:07
  • 3
    @Dolphin ScrollViews are used to have only one children (e.g. vertical LinearLayout), and ScrollView's default height is based on the content inside LinearLayout. Let's say LinearLayout has three 30dp height views inside, then ScrollView's height now is 90dp. If you put the paremeter (android:fillViewport="true") then it's gonna make the height match/fill the parent. – felippe Jul 16 '15 at 04:39
  • 1
    no luck for me :( http://stackoverflow.com/questions/33863067/why-view-pager-doesnt-work-in-scrollview?noredirect=1#comment55491222_33863067 – Tony Nov 23 '15 at 03:57
  • hail the saviour. – Pranav Kumar May 28 '17 at 09:34
  • is this issue fixed?. I still have same issue and posted new question also in https://stackoverflow.com/questions/59056540/scrollview-not-working-when-it-has-viewpager-in-android – Ashok Reddy Narra Nov 28 '19 at 11:35
27

Yes it is possible to have viewpager inside scrollview.

Use below code and you will achieve your goal, i have done the same in my code as well.

 public class WrapContentHeightViewPager extends ViewPager {

    public WrapContentHeightViewPager(Context context) {
        super(context);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int height = 0;

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);

            child.measure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));

            int h = child.getMeasuredHeight();

            if (h > height) height = h;
        }

        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
 }

 @Override
 public boolean onTouch(View v, MotionEvent event) {
    int dragthreshold = 30;

    int downX = 0;

    int downY = 0;

    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) {
                mViewPager.getParent().requestDisallowInterceptTouchEvent(false);

                mScrollView.getParent().requestDisallowInterceptTouchEvent(true);
            } else if (distanceX > distanceY && distanceX > dragthreshold) {
                mViewPager.getParent().requestDisallowInterceptTouchEvent(true);

                mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
            }

            break;
        case MotionEvent.ACTION_UP:
            mScrollView.getParent().requestDisallowInterceptTouchEvent(false);

            mViewPager.getParent().requestDisallowInterceptTouchEvent(false);

            break;
    }

    return false;
 }

Hope this aids someone in resolving scroll issues with nested ViewPager within a ScrollView.

worked
  • 5,762
  • 5
  • 54
  • 79
Silvans Solanki
  • 1,267
  • 1
  • 14
  • 27
  • 1
    Awesome! This worked. The only minor gotcha is that all pages now have the same height (meaning the scollable area height is set the the page with the largest height). Any way to fix this? – friederbluemle Jan 17 '16 at 10:14
  • If you guys down vote this answer then please specify the reason for the same, so other can get to know disadvantage of using this solution. Thanks – Silvans Solanki Aug 30 '16 at 10:13
  • 1
    I've down voted your solution because if you have different content sizes within the view pager's children views you're always getting the biggest one. To get a more accurate solution for this type of problem, use this solution provided by @Abhishek V: http://stackoverflow.com/a/32410274/3767765 – RicardoSousaDev Oct 31 '16 at 13:31
  • 1
    @RicardoSousaDev Thank you for identifying the issue, but instead of downvote my answer you could have improve it with those methods and make it more better answer. Because always people dont read all the comments written in the answer. – Silvans Solanki Nov 03 '16 at 07:10
  • I used only WrapContentHeightViewPager without Override and it works. Thank you. – Michalsx Mar 14 '17 at 15:53
  • @Michalsx glad to know it works well and help you achieve your goal. – Silvans Solanki Mar 15 '17 at 08:12
3

This solution worked for me, with dynamic content.

http://www.henning.ms/2013/09/09/viewpager-that-simply-dont-measure-up/

In the xml ScrollView put android:fillViewport=true and android:layout_height=wrap_content

And in custom ViewPager too put android:layout_height=wrap_content

Roses
  • 350
  • 5
  • 13
1

In case someone is having the same problem, when I use the class below it works fine.

class CustomWrapContentViewPager(cont: Context, attr: AttributeSet?) : ViewPager(cont,attr) {

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    var height = 0
    for (i in 0 until childCount) {
        val child: View = getChildAt(i)
        child.measure(
            widthMeasureSpec,
            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
        )
        val h: Int = child.measuredHeight
        if (h > height) height = h
    }
    val heightMeasure = MeasureSpec.makeMeasureSpec(
        height,
        MeasureSpec.EXACTLY
    )
    super.onMeasure(widthMeasureSpec, heightMeasure)
    }
}
David Buck
  • 3,752
  • 35
  • 31
  • 35