Android:我无法拥有ViewPager WRAP_CONTENT

Ada*_*dam 245 android

我已经设置了一个简单的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);
Run Code Online (Sandbox Code Playgroud)

尽管将高度设置为wrap_content,但即使imageview仅为200dp,寻呼机也会填充屏幕.我试图用"200"替换寻呼机的高度,但这给了我多种分辨率的不同结果.我无法将"dp"添加到该值.如何在寻呼机的布局中添加200dp?

Dan*_*lle 390

ViewPager按照以下方式覆盖你的测量将使它达到目前最大的孩子的身高.

@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;
    }

    if (height != 0) {
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    }

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Run Code Online (Sandbox Code Playgroud)

  • 这最接近我的需要,但有两件事需要补充:***1.***ViewPager只调整其实际子项中最大的一个,即只有当前可见的项目和直接相邻的项目.在ViewPager上调用setOffscreenPageLimit(子项总数)可以解决这个问题,并生成一个ViewPager,其大小设置为其所有项目中的最大项,并且从不调整大小.***2.***WebViews在尝试测量它们时会遇到一些奇怪的问题.在加载某些内容后,在WebView上调用requestLayout()解决了这个问题. (21认同)
  • 你已经在ViewPager上执行了setAdapter(),getChildCount()可能会返回0!实际的populate()调用(创建视图)发生在super.onMeasure(widthMeasureSpec,heightMeasureSpec)中; 呼叫.在此函数的开头添加额外的super.onMeasure()调用就可以了.另请查看http://stackoverflow.com/questions/38492210/viewpager-getchildcount-returns-zero (7认同)
  • 如果你有装饰子视图,这将不起作用 - 这是因为ViewPager.onMeasure()测量装饰视图并首先为它们分配空间,然后将剩余的空间给予非装饰儿童.尽管如此,这是迄今为止最不正确的解决方案,所以我投了赞成票;) (4认同)
  • 我将要修复一个小问题:如果viewPager具有GONE的可见性并且您将其设置为可见,则onMeasure在其片段创建之前被调用.所以最终的高度为0.如果有人有想法,他会受到欢迎.我想我会在创建片段时使用回调函数 (3认同)
  • 每次使用ViewPager时我都会回到这里 (3认同)
  • 为什么我要让孩子的身高最高?如果我滚动到相邻的孩子,它将使视图有很多额外的空间,看起来很有趣.例如,我在第3页,共5页,高度为32,我移动到第2页,高度为8.现在,第2页的高度为32,但内容甚至不适合. (3认同)
  • 在摆弄这个问题超过6个小时之后,你的代码成了我的一天!非常感谢! (2认同)

cyb*_*gen 101

另一个更通用的解决方案是开始wrap_content工作.

我已经扩展ViewPager到覆盖onMeasure().高度围绕第一个子视图.如果子视图的高度不完全相同,这可能会导致意外结果.为此,可以轻松扩展该类,让我们说动画为当前视图/页面的大小.但我不需要那个.

您可以像原始ViewPager一样在您的XML布局中使用此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"/>
Run Code Online (Sandbox Code Playgroud)

优点:此方法允许在包括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;
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 当viewpager被破坏并再次打开时,是否有其他人在当前项目旁边有空白页? (31认同)
  • 你只需要合并这个问题的两个最佳答案,如我的博客中所述:http://pristalovpavel.wordpress.com/2014/12/26/doing-it-right-vertical-scrollview-with-viewpager-and-listview / (10认同)
  • 只需将'onMeasure'方法的代码替换为'DanielLópezLacalle'给出的答案. (4认同)
  • 我有空白页面 (2认同)

Min*_*Man 56

我的答案基于DanielLópezLacalle和这篇文章http://www.henning.ms/2013/09/09/viewpager-that-simply-dont-measure-up/.Daniel的答案的问题在于,在某些情况下,我的孩子的身高为零.不幸的是,解决方案是两次测量.

@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);
}
Run Code Online (Sandbox Code Playgroud)

如果您想要或只是wrap_content,这也允许您在ViewPager上设置高度.

  • 我简直不敢相信!我花了 2 天的时间将我的自定义 viewpager 粘在一起,但遇到了一个问题,当我的初始视图不显示时,我就是不知道为什么!`// super 必须在开始时调用,以便可以初始化子视图。` &lt;----- 这就是原因,必须在 onMeasure 函数的开始和结束时调用它。Yippiii,今天对我进行虚拟击掌! (3认同)

Jav*_*ave 38

我刚才回答了一个非常相似的问题,当找到一个链接来支持我的索赔时碰巧找到了这个,很幸运你:)

我的另一个答案:
ViewPager不支持,wrap_content因为它(通常)不会同时加载所有子代,因此无法获得合适的大小(选项是每次切换时都有一个改变大小的寻呼机页).

但是,您可以设置精确的尺寸(例如150dp)并且match_parent也可以.
您还可以通过更改其中的height-attribute来动态修改维度LayoutParams.

根据您的需要,您可以在自己的xml文件中创建ViewPager,并将layout_height设置为200dp,然后在您的代码中,而不是从头开始创建新的ViewPager,您可以膨胀该xml文件:

LayoutInflater inflater = context.getLayoutInflater();
inflater.inflate(R.layout.viewpagerxml, layout, true);
Run Code Online (Sandbox Code Playgroud)

  • @ChrisVandevelde这似乎是一些android库的常见租户.一旦你学会了基础知识,你就会发现什么都没有 (8认同)
  • 好的答案,有点恼人,默认行为是"做一些有些不可理解的事情".感谢您的解释. (3认同)

Raa*_*nan 16

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

https://github.com/rnevet/WCViewPager

解决方案的灵感来自于此处的一些答案,但改进了:

  • 根据当前视图动态更改ViewPager高度,包括滚动时.
  • 考虑像PagerTabStrip这样的"装饰"视图的高度.
  • 考虑所有Padding.

更新了支持库版本24,它破坏了以前的实现.


Ida*_*dan 15

我刚刚碰到了同样的问题.我有一个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>
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,你不需要xmlns:android ="http://schemas.android.com/apk/res/android",只在第一个. (4认同)
  • 你的问题根本不一样.您的布局可以正常工作,ViewPager设置为match_parent - OP有一种情况,他希望ViewPager包装到它的内容. (2认同)

Fel*_*hos 12

使用DanielLópezLocalle回答,我在Kotlin创建了这个课程.希望它能为您节省更多时间

class DynamicHeightViewPager @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : ViewPager(context, attrs) {

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    var heightMeasureSpec = heightMeasureSpec

    var height = 0
    for (i in 0 until childCount) {
        val child = getChildAt(i)
        child.measure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
        val h = child.measuredHeight
        if (h > height) height = h
    }

    if (height != 0) {
        heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
    }

    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}}
Run Code Online (Sandbox Code Playgroud)


log*_*gan 9

我也碰到了这个问题,但在我的情况下,我有一个FragmentPagerAdapter提供ViewPager它的页面.我的问题是,onMeasure()ViewPager任何被调用前Fragments已经建立(因此不能大小本身正确).

有点试验和错误之后,我发现finishUpdate()后的FragmentPagerAdapter的方法被调用Fragments已(从被初始化instantiateItem()FragmentPagerAdapter),并且还经过/页面翻滚过程中.我做了一个小界面:

public interface AdapterFinishUpdateCallbacks
{
    void onFinishUpdate();
}
Run Code Online (Sandbox Code Playgroud)

我传入我的FragmentPagerAdapter电话:

@Override
public void finishUpdate(ViewGroup container)
{
    super.finishUpdate(container);

    if (this.listener != null)
    {
        this.listener.onFinishUpdate();
    }
}
Run Code Online (Sandbox Code Playgroud)

这反过来让我可以调用setVariableHeight()我的CustomViewPager实现:

public void setVariableHeight()
{
    // super.measure() calls finishUpdate() in adapter, so need this to stop infinite loop
    if (!this.isSettingHeight)
    {
        this.isSettingHeight = true;

        int maxChildHeight = 0;
        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
        for (int i = 0; i < getChildCount(); i++)
        {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.UNSPECIFIED));
            maxChildHeight = child.getMeasuredHeight() > maxChildHeight ? child.getMeasuredHeight() : maxChildHeight;
        }

        int height = maxChildHeight + getPaddingTop() + getPaddingBottom();
        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.measure(widthMeasureSpec, heightMeasureSpec);
        requestLayout();

        this.isSettingHeight = false;
    }
}
Run Code Online (Sandbox Code Playgroud)

我不确定这是最好的方法,如果你认为它好/坏/邪恶会喜欢评论,但它似乎在我的实现中工作得很好:)

希望这有助于那里的人!

编辑:我忘了添加一个requestLayout()后调用super.measure()(否则它不重绘视图).

我也忘了将父级的填充添加到最终高度.

我还保留原始宽度/高度MeasureSpecs,有利于根据需要创建一个新的.已相应更新了代码.

我遇到的另一个问题是,它不会在a中正确调整大小,ScrollView并发现罪魁祸首是用MeasureSpec.EXACTLY而不是测量孩子MeasureSpec.UNSPECIFIED.更新以反映这一点.

这些更改都已添加到代码中.如果需要,您可以查看历史记录以查看旧版本(不正确).


Bla*_*hex 8

另一种解决方案是ViewPager根据当前页面高度更新高度PagerAdapter.假设您以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;
}
Run Code Online (Sandbox Code Playgroud)

哪里mPagesPageInfo动态添加到结构的内部结构列表,PagerAdapter并且CustomImageView只是常规ImageView的覆盖onMeasure()方法,根据指定的宽度设置其高度并保持图像宽高比.

您可以ViewPagersetPrimaryItem()方法中强制高度:

@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)));
}
Run Code Online (Sandbox Code Playgroud)

请注意Math.max(height, 1).这修复了烦人的错误,ViewPager它不会更新显示的页面(显示为空白),当前一页的高度为零(即可绘制空值CustomImageView)时,每个奇数在两页之间来回滑动.


Kir*_*kov 6

在viewpager中使用静态内容并且您不希望使用任何花哨的动画时,您可以使用以下视图分页器

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));
  }
}
Run Code Online (Sandbox Code Playgroud)


Woj*_*lik 5

改进了Daniel L\xc3\xb3pez Lacalle答案,用Kotlin重写:

\n\n
class MyViewPager(context: Context, attrs: AttributeSet): ViewPager(context, attrs) {\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val zeroHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)\n\n        val maxHeight = children\n            .map { it.measure(widthMeasureSpec, zeroHeight); it.measuredHeight }\n            .max() ?: 0\n\n        if (maxHeight > 0) {\n            val maxHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY)\n            super.onMeasure(widthMeasureSpec, maxHeightSpec)\n            return\n        }\n\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n