我已经设置了一个简单的ViewPager,它在每个页面上都有一个高度为200dp的ImageView。

这是我的寻呼机:

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

尽管高度设置为wrap_content,但即使imageview只有200dp,分页器也总是充满屏幕。我尝试用“200”替换寻呼机的高度,但在不同分辨率下会得到不同的结果。我无法将“dp”添加到该值。如何将200dp添加到寻呼机的布局?


当前回答

测量ViewPager的高度:

public class WrapViewPager extends ViewPager {
    View primaryView;

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (primaryView != null) {
            int height = 0;
            for (int i = 0; i < getChildCount(); i++) {
                if (primaryView == getChildAt(i)) {
                    int childHeightSpec = MeasureSpec.makeMeasureSpec(0x1 << 30 - 1, MeasureSpec.AT_MOST);
                    getChildAt(i).measure(widthMeasureSpec, childHeightSpec);
                    height = getChildAt(i).getMeasuredHeight();
                }

            }

            setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        }
    }

    public void setPrimaryView(View view) {
        primaryView = view;
    }

}

调用setPrimaryView(视图):

public class ZGAdapter extends PagerAdapter {

    @Override
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        super.setPrimaryItem(container, position, object);
        ((WrapViewPager)container).setPrimaryView((View)object);
    }

}

其他回答

另一个更通用的解决方案是让wrap_content正常工作。

我已经扩展了ViewPager来覆盖onMeasure()。高度被包裹在第一个子视图周围。如果子视图的高度不完全相同,这可能会导致意想不到的结果。因此,类可以很容易地扩展,让我们说动画到当前视图/页面的大小。但我不需要。

你可以在yout XML布局中使用这个ViewPager,就像原来的ViewPager一样:

<view
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    class="de.cybergen.ui.layout.WrapContentHeightViewPager"
    android:id="@+id/wrapContentHeightViewPager"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"/>

优点:这种方法允许在任何布局(包括RelativeLayout)中使用ViewPager来覆盖其他ui元素。

还有一个缺点:如果你想使用页边距,你必须创建两个嵌套的布局,并给内部的一个指定所需的页边距。

代码如下:

public class WrapContentHeightViewPager extends ViewPager {

    /**
     * Constructor
     *
     * @param context the context
     */
    public WrapContentHeightViewPager(Context context) {
        super(context);
    }

    /**
     * Constructor
     *
     * @param context the context
     * @param attrs the attribute set
     */
    public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

        // find the first child view
        View view = getChildAt(0);
        if (view != null) {
            // measure the first child view with the specified measure spec
            view.measure(widthMeasureSpec, heightMeasureSpec);
        }

        setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, view));
    }

    /**
     * Determines the height of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @param view the base view with already measured height
     *
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec, View view) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            // set the height from the base view if available
            if (view != null) {
                result = view.getMeasuredHeight();
            }
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

}

另一种解决方案是根据PagerAdapter中的当前页面高度更新ViewPager高度。假设你用这种方式创建ViewPager页面:

@Override
public Object instantiateItem(ViewGroup container, int position) {
  PageInfo item = mPages.get(position);
  item.mImageView = new CustomImageView(container.getContext());
  item.mImageView.setImageDrawable(item.mDrawable);
  container.addView(item.mImageView, 0);
  return item;
}

其中,mPages是动态添加到PagerAdapter的PageInfo结构的内部列表,CustomImageView只是常规的ImageView,覆盖onMeasure()方法,根据指定的宽度设置其高度并保持图像纵横比。

你可以在setPrimaryItem()方法中强制ViewPager的高度:

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
  super.setPrimaryItem(container, position, object);

  PageInfo item = (PageInfo) object;
  ViewPager pager = (ViewPager) container;
  int width = item.mImageView.getMeasuredWidth();
  int height = item.mImageView.getMeasuredHeight();
  pager.setLayoutParams(new FrameLayout.LayoutParams(width, Math.max(height, 1)));
}

注意数学。这修复了ViewPager不更新显示的页面(显示为空白),当前一页的高度为零时(即CustomImageView中可绘制的空),在两页之间每次奇怪的来回滑动。

我已经在几个项目中遇到过这个问题,从来没有一个完整的解决方案。所以我创建了一个WrapContentViewPager github项目作为ViewPager的替代。

https://github.com/rnevet/WCViewPager

解决方案的灵感来自这里的一些答案,但改进了:

根据当前视图(包括滚动时)动态更改ViewPager高度。 考虑像PagerTabStrip这样的“装饰”视图的高度。 考虑所有填充。

更新到支持库版本24,打破了以前的实现。

我遇到了同样的问题,当用户在页面之间滚动时,我还必须让ViewPager环绕其内容。使用cybergen上面的答案,我定义onMeasure方法如下:

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

    if (getCurrentItem() < getChildCount()) {
        View child = getChildAt(getCurrentItem());
        if (child.getVisibility() != GONE) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
                    MeasureSpec.UNSPECIFIED);
            child.measure(widthMeasureSpec, heightMeasureSpec);
        }

        setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, getChildAt(getCurrentItem())));            
    }
}

这样,onMeasure方法设置ViewPager显示的当前页面的高度。

我也遇到了同样的问题。我有一个ViewPager,我想在它的按钮上显示一个广告。我发现的解决方案是让寻呼机进入一个RelativeView,并设置它的layout_above的视图id,我想看到它下面。这对我很管用。

这是我的布局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>