3

I have a ViewPager implemented without fragment (just view). Each page contains a list of elements, but the list hasn't always the same size.

I tried to set the ViewPager dimension with this code (extends ViewPager so I can override this function) :

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

        int height = 0;
        for(int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            if(h > height) height = h;
        }

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

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

The problem is that it set the same height for every page. I need a custom height for each page, depending of the height of the list.

I also tried to use setOnPageChangeListener calling requestLayout() and invalidate() on the ViewPager, but the size remains the same. Maybe I did it wrong...

Here is some code that might be useful :

A part of my PagerAdapter:

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            // Inflate a new layout from our resources
            View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_home_pager,
                    container, false);

            mContainerView = (ViewGroup) view.findViewById(R.id.news_container);
            reloadNews(position);

            // Add the newly created View to the ViewPager
            container.addView(view);

            // Return the View
            return view;
        }

A part of my XML layout:

        <ScrollView
            android:id="@+id/news_scrollview"
            android:layout_width="match_parent"
            android:layout_height="fill_parent">

            <com.ViewPagerWithHeight
                android:id="@+id/viewpager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@android:color/white"/>

        </ScrollView>

Thanks of lot for helping me,

DavidL
  • 1,120
  • 1
  • 15
  • 34
  • Why do you need a different height for each page? What are you trying to accomplish? – Andrew Orobator Mar 11 '15 at 03:10
  • On each page, I have a list of news (to simplify: a page is like a category). The number of news is different for each page. The ViewPager is inside a ScrollView. Because this code set the same size for each page (the biggest one), I'm able to scroll down even when there is no element to scroll (got some blank space). – DavidL Mar 11 '15 at 03:17
  • Could you flip your layout and instead have the ScrollView inside the ViewPager? – stkent Mar 11 '15 at 12:25
  • Try the below answer. I have tried it and it is working http://stackoverflow.com/a/32410274/4792879 – Nikhil Gupta Jun 29 '16 at 13:54
  • The answer at link below seems to work perfectly for this purpose : http://stackoverflow.com/a/32410274/4792879 – Nikhil Gupta Jun 29 '16 at 13:56

2 Answers2

1

The ViewPager height cannot vary per-page as you are hoping. You could attempt to subclass ViewPager to add this ability, but handling the visual transitions between differently-sized pages is going to be very fiddly. Another alternative may be to re-measure the ViewPager on every page change (but I would expect the transition to look pretty bad). You would need to set a page change listener, then call requestLayout on the ViewPager. Your ViewPager onMeasure would need to find the height of the currently-visible child only, and set the ViewPager height to match this.

Aside: I don't see you calling setMeasuredDimension anywhere. When measuring a view, you must call this method with your calculated width and height to inform the view what size it has been measured at. Currently you are calling through to super.onMeasure at the end of your custom onMeasure which, while valid, means the ViewPager will always be measured with the "default" width and height. In this case, that happens to match the height of the largest page anyway.

See the docs for onMeasure for more details. In particular:

CONTRACT: When overriding this method, you must call setMeasuredDimension(int, int) to store the measured width and height of this view. Failure to do so will trigger an IllegalStateException, thrown by measure(int, int). Calling the superclass' onMeasure(int, int) is a valid use.

stkent
  • 19,772
  • 14
  • 85
  • 111
  • I just tried it. Still the same issue : the chosen height is the biggest one, and it is applied to all pages... Even if you suggest another way to set the measured dimension, the height value remains the same. – DavidL Mar 11 '15 at 12:10
  • Thanks for the clarification, editing answer to reflect this. It makes sense, I suppose, if you think about how a ViewPager works. We're setting the overall ViewPager size this way. So my thoughts are: (1) it's not possible to achieve what you are looking for using the default ViewPager; (2) it's not clear to me at what point during a page transition a ViewPager could 'change height' without it looking weird; and (3) you could try overriding ViewPager and handling the size-per-page yourself, but handling case (2) is likely to be a lot of effort. – stkent Mar 11 '15 at 12:20
  • Just added another suggestion to the answer - it's not brilliant, but it might be a start. – stkent Mar 11 '15 at 12:29
0

I just find out something that do the job.

I moved the Scrollview inside the pageItem.xml so I can set the page size (for all pages) but still can scroll if the list is long enough. The ViewPager is now inside a FrameLayout with layout_height = fill_parent.

The page size is fixed, but the scrollview do the trick...

The ViewPager (inside my fragment) :

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:id="@+id/viewpagercontainer">

    <com.ViewPagerWithHeight
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"/>

</FrameLayout>

The "Page item" :

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/news_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:showDividers="middle"
        android:divider="?android:dividerHorizontal" />

</ScrollView>

onMeasure function:

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

    heightMeasureSpec = MeasureSpec.makeMeasureSpec(containerHeight, MeasureSpec.EXACTLY);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

How to set the attribute containerHeight (more info here):

final View pagerContainer = (View) mView.findViewById(R.id.viewpagercontainer);
pagerContainer.post(new Runnable() {
    @Override
    public void run() {
        mViewPager.setContainerHeight(pagerContainer.getMeasuredHeight());
    }
});
Community
  • 1
  • 1
DavidL
  • 1,120
  • 1
  • 15
  • 34