Android: I am unable to have ViewPager WRAP_CONTEN

2018-12-31 21:12发布

I have setup a simple ViewPager that has an ImageView with a height of 200dp on each page.

Here is my pager:

pager = new ViewPager(this);
pager.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
pager.setBackgroundColor(Color.WHITE);
pager.setOnPageChangeListener(listener);
layout.addView(pager);

Despite the height set as wrap_content, the pager always fills the screen even though the imageview is only 200dp. I tried to replace the height of the pager with "200" but that gives me different results with multiple resolutions. I am unable to add "dp" to that value. How do I add 200dp to the pager's layout?

标签: android
22条回答
看风景的人
2楼-- · 2018-12-31 21:47

I was just answering a very similar question about this, and happened to find this when looking for a link to back up my claims, so lucky you :)

My other answer:
The ViewPager does not support wrap_content as it (usually) never have all its children loaded at the same time, and can therefore not get an appropriate size (the option would be to have a pager that changes size every time you have switched page).

You can however set a precise dimension (e.g. 150dp) and match_parent works as well.
You can also modify the dimensions dynamically from your code by changing the height-attribute in its LayoutParams.

For your needs you can create the ViewPager in its own xml-file, with the layout_height set to 200dp, and then in your code, rather than creating a new ViewPager from scratch, you can inflate that xml-file:

LayoutInflater inflater = context.getLayoutInflater();
inflater.inflate(R.layout.viewpagerxml, layout, true);
查看更多
余欢
3楼-- · 2018-12-31 21:48

If the ViewPager you're using is a child of a ScrollView AND has a PagerTitleStrip child you'll need to use a slight modification of the great answers already provided. For reference my XML looks like this:

<ScrollView
    android:id="@+id/match_scroll_view"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white">

    <LinearLayout
        android:id="@+id/match_and_graphs_wrapper"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <view
            android:id="@+id/pager"
            class="com.printandpixel.lolhistory.util.WrapContentHeightViewPager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <android.support.v4.view.PagerTitleStrip
                android:id="@+id/pager_title_strip"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="top"
                android:background="#33b5e5"
                android:paddingBottom="4dp"
                android:paddingTop="4dp"
                android:textColor="#fff" />
        </view>
    </LinearLayout>
</ScrollView>

In your onMeasure you have to ADD the measuredHeight of the PagerTitleStrip if one is found. Otherwise its height won't be considered into the largest height of all the children even though it takes up additional space.

Hope this helps someone else. Sorry that it's a bit of a hack...

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) {
        int pagerTitleStripHeight = 0;
        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) {
                // get the measuredHeight of the tallest fragment
                height = h;
            }
            if (child.getClass() == PagerTitleStrip.class) {
                // store the measured height of the pagerTitleStrip if one is found. This will only
                // happen if you have a android.support.v4.view.PagerTitleStrip as a direct child
                // of this class in your XML.
                pagerTitleStripHeight = h;
            }
        }

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

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}
查看更多
无色无味的生活
4楼-- · 2018-12-31 21:49

I based my answer on Daniel López Lacalle and this post http://www.henning.ms/2013/09/09/viewpager-that-simply-dont-measure-up/. The problem with Daniel's answer is that in some cases my children had a height of zero. The solution was to unfortunately measure twice.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int mode = MeasureSpec.getMode(heightMeasureSpec);
    // Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT.
    // At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT.
    if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
        // super has to be called in the beginning so the child views can be initialized.
        super.onMeasure(widthMeasureSpec, 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 has to be called again so the new specs are treated as exact measurements
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

This also lets you set a height on the ViewPager if you so want to or just wrap_content.

查看更多
初与友歌
5楼-- · 2018-12-31 21:49

I just bumped into the same issue. I had a ViewPager and I wanted to display an ad at the button of it. The solution I found was to get the pager into a RelativeView and set it's layout_above to the view id i want to see below it. that worked for me.

here is my layout XML:

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

    <LinearLayout
        android:id="@+id/AdLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical" >
    </LinearLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/mainpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/AdLayout" >
    </android.support.v4.view.ViewPager>
</RelativeLayout>
查看更多
何处买醉
6楼-- · 2018-12-31 21:51

in my case adding clipToPadding solved the problem.

<android.support.v4.view.ViewPager
    ...
    android:clipToPadding="false"
    ...
    />

Cheers!

查看更多
看风景的人
7楼-- · 2018-12-31 21:54

When using static content inside the viewpager and you wish no fancy animation you may use the following view pager

public class HeightWrappingViewPager extends ViewPager {

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

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

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)   {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
      View firstChild = getChildAt(0);
      firstChild.measure(widthMeasureSpec, heightMeasureSpec);
      super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(firstChild.getMeasuredHeight(), MeasureSpec.EXACTLY));
  }
}
查看更多
登录 后发表回答