Android: no puedo tener ViewPager WRAP_CONTENT

Configuré un ViewPager simple que tiene un ImageView con una altura de 200dp en cada página.

Aquí está mi buscapersonas:

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

A pesar de la altura establecida como wrap_content, el buscapersonas siempre llena la pantalla a pesar de que la vista de la imagen es de solo 200dp. Traté de reemplazar la altura del localizador con “200”, pero eso me da resultados diferentes con múltiples resoluciones. No puedo agregar “dp” a ese valor. ¿Cómo agrego 200dp al diseño del buscapersonas?

Reemplazar onMeasure de su ViewPager siguiente manera lo hará obtener la altura del hijo más grande que tiene actualmente.

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

Otra solución más genérica es conseguir que wrap_content simplemente funcione.

ViewPager para anular onMeasure() . La altura está envuelta alrededor de la primera vista de niño. Esto podría generar resultados inesperados si las vistas secundarias no tienen exactamente la misma altura. Para eso la clase se puede extender fácilmente, digamos animada al tamaño de la vista / página actual. Pero no necesitaba eso.

Puede usar este ViewPager en sus diseños XML al igual que el ViewPager original:

  

Ventaja: este enfoque permite usar ViewPager en cualquier diseño, incluido RelativeLayout, para superponer otros elementos de la interfaz de usuario.

Queda un inconveniente: si desea usar márgenes, debe crear dos diseños nesteds y otorgarle al interno los márgenes deseados.

Aquí está el código:

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

Basé mi respuesta en Daniel López Lacalle y en esta publicación http://www.henning.ms/2013/09/09/viewpager-that-simply-dont-measure-up/ . El problema con la respuesta de Daniel es que en algunos casos mis hijos tenían una altura de cero. La solución fue desafortunadamente medir dos veces.

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

Esto también le permite establecer una altura en ViewPager si lo desea o solo wrap_content.

Estaba respondiendo una pregunta muy similar sobre esto, y sucedió que encontré esto cuando busqué un enlace para respaldar mis afirmaciones, con tanta suerte 🙂

Mi otra respuesta:
ViewPager no admite wrap_content ya que (normalmente) nunca tiene todos sus hijos cargados al mismo tiempo y, por lo tanto, no puede obtener el tamaño adecuado (la opción sería tener un buscapersonas que cambie de tamaño cada vez que cambie de página).

Sin embargo, puede establecer una dimensión precisa (por ejemplo, 150dp) y match_parent también.
También puede modificar dinámicamente las dimensiones desde su código cambiando el atributo de height en sus LayoutParams .

Para sus necesidades , puede crear ViewPager en su propio archivo xml, con layout_height establecido en 200dp, y luego en su código, en lugar de crear un nuevo ViewPager desde cero, puede inflar ese archivo xml:

 LayoutInflater inflater = context.getLayoutInflater(); inflater.inflate(R.layout.viewpagerxml, layout, true); 

Ya me he enfrentado a este problema en varios proyectos y nunca tuve una solución completa. Así que creé un proyecto WrapContentViewPager github como reemplazo in situ para ViewPager.

https://github.com/rnevet/WCViewPager

La solución se inspiró en algunas de las respuestas aquí, pero mejora en:

  • Cambia dinámicamente la altura de ViewPager según la vista actual, incluso mientras se desplaza.
  • Toma en consideración la altura de las vistas de “decoración” como PagerTabStrip.
  • Toma en consideración todo el relleno.

Actualizado para la biblioteca de soporte versión 24 que rompió la implementación anterior.

Me topé con el mismo problema. Tenía un ViewPager y quería mostrar un anuncio en el botón. La solución que encontré fue colocar el buscapersonas en un RelativeView y configurar su layout_above para el id de la vista que quiero ver debajo de él. eso funcionó para mí.

aquí está mi diseño XML:

        

Otra solución es actualizar la altura de ViewPager acuerdo con la altura de la página actual en su PagerAdapter . Asumiendo que estás creando tus páginas de ViewPager esta manera:

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

Donde mPages es una lista interna de estructuras de PageInfo añadidas dinámicamente a PagerAdapter y CustomImageView es simplemente ImageView regular con el método onMeasure() que establece su altura de acuerdo con el ancho especificado y mantiene la relación de aspecto de la imagen.

Puede forzar la altura de setPrimaryItem() en el método setPrimaryItem() :

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

Tenga en cuenta el Math.max(height, 1) . Eso soluciona el molesto error de que ViewPager no actualiza la página mostrada (lo muestra en blanco), cuando la página anterior tiene altura cero (es decir, nula CustomImageView en el CustomImageView ), cada deslizamiento impar de ida y vuelta entre dos páginas.

También encontré este problema, pero en mi caso tenía un FragmentPagerAdapter que estaba suministrando ViewPager con sus páginas. El problema que tuve fue que se onMeasure() de ViewPager antes de que se hubiera creado cualquiera de los Fragments (y por lo tanto no podía dimensionarse correctamente).

Después de un poco de prueba y error, encontré que se finishUpdate() método finishUpdate() del FragmentPagerAdapter después de que los Fragments se hayan inicializado (desde instantiateItem() en FragmentPagerAdapter ), y también después / durante el desplazamiento de la página. Hice una pequeña interfaz:

 public interface AdapterFinishUpdateCallbacks { void onFinishUpdate(); } 

que paso a mi FragmentPagerAdapter y llamo a:

 @Override public void finishUpdate(ViewGroup container) { super.finishUpdate(container); if (this.listener != null) { this.listener.onFinishUpdate(); } } 

lo que a su vez me permite llamar a setVariableHeight() en mi implementación de 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; } } 

No estoy seguro de que sea el mejor enfoque, me encantaría comentar si piensas que es bueno / malo / malo, pero parece estar funcionando bastante bien en mi implementación 🙂

Espero que esto ayude a alguien por ahí!

EDITAR: Olvidé agregar un requestLayout() después de llamar a super.measure() (de lo contrario, no vuelve a dibujar la vista).

También olvidé agregar el relleno de los padres a la altura final.

También dejé de mantener el ancho / alto original MeasureSpecs a favor de crear uno nuevo según sea necesario. Han actualizado el código en consecuencia.

Otro problema que tuve fue que no se dimensionaría correctamente en ScrollView y descubrí que el culpable era medir al niño con MeasureSpec.EXACTLY lugar de MeasureSpec.UNSPECIFIED . Actualizado para reflejar esto.

Todos estos cambios se han agregado al código. Puede verificar el historial para ver las versiones antiguas (incorrectas) si lo desea.

Al utilizar contenido estático dentro del visor y no desea una animación elegante, puede usar el siguiente buscador de vistas

 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)); } } 
 public CustomPager (Context context) { super(context); } public CustomPager (Context context, AttributeSet attrs) { super(context, attrs); } int getMeasureExactly(View child, int widthMeasureSpec) { child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); int height = child.getMeasuredHeight(); return MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST; final View tab = getChildAt(0); if (tab == null) { return; } int width = getMeasuredWidth(); if (wrapHeight) { // Keep the current measured width. widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); } Fragment fragment = ((Fragment) getAdapter().instantiateItem(this, getCurrentItem())); heightMeasureSpec = getMeasureExactly(fragment.getView(), widthMeasureSpec); //Log.i(Constants.TAG, "item :" + getCurrentItem() + "|height" + heightMeasureSpec); // super has to be called again so the new specs are treated as // exact measurements. super.onMeasure(widthMeasureSpec, heightMeasureSpec); } 

Me encontré con el mismo problema, y ​​también tuve que hacer que ViewPager envolviera sus contenidos cuando el usuario se desplazaba entre las páginas. Usando la respuesta anterior de cybergen, definí el método onMeasure de la siguiente manera:

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

De esta forma, el método onMeasure establece el alto de la página actual que muestra ViewPager.

Desde el código fuente de la aplicación de android de Popcorn, encontré esta solución que ajusta dinámicamente el tamaño del viewpager con una buena animación, dependiendo del tamaño del elemento secundario actual.

https://git.popcorntime.io/popcorntime/android/blob/5934f8d0c8fed39af213af4512272d12d2efb6a6/mobile/src/main/java/pct/droid/widget/WrappingViewPager.java

 public class WrappingViewPager extends ViewPager { private Boolean mAnimStarted = false; public WrappingViewPager(Context context) { super(context); } public WrappingViewPager(Context context, AttributeSet attrs){ super(context, attrs); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if(!mAnimStarted && null != getAdapter()) { int height = 0; View child = ((FragmentPagerAdapter) getAdapter()).getItem(getCurrentItem()).getView(); if (child != null) { child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); height = child.getMeasuredHeight(); if (VersionUtils.isJellyBean() && height < getMinimumHeight()) { height = getMinimumHeight(); } } // Not the best place to put this animation, but it works pretty good. int newHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); if (getLayoutParams().height != 0 && heightMeasureSpec != newHeight) { final int targetHeight = height; final int currentHeight = getLayoutParams().height; final int heightChange = targetHeight - currentHeight; Animation a = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { if (interpolatedTime >= 1) { getLayoutParams().height = targetHeight; } else { int stepHeight = (int) (heightChange * interpolatedTime); getLayoutParams().height = currentHeight + stepHeight; } requestLayout(); } @Override public boolean willChangeBounds() { return true; } }; a.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { mAnimStarted = true; } @Override public void onAnimationEnd(Animation animation) { mAnimStarted = false; } @Override public void onAnimationRepeat(Animation animation) { } }); a.setDuration(1000); startAnimation(a); mAnimStarted = true; } else { heightMeasureSpec = newHeight; } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } 

Nada de lo sugerido anteriormente funcionó para mí. Mi caso de uso es tener 4 ViewPagers personalizados en ScrollView . La parte superior de ellos se mide en función de la relación de aspecto y el rest simplemente tiene layout_height=wrap_content . Probé las soluciones de cibergen , Daniel López Lacalle . Ninguno de ellos funciona completamente para mí.

Adivino por qué el cibergenético no funciona en la página> 1 es porque calcula la altura del buscapersonas según la página 1, que está oculta si se desplaza más.

Tanto las sugerencias de cibergen como las de Daniel López Lacalle tienen un comportamiento raro en mi caso: 2 de 3 se cargan bien y 1 altura aleatoria es 0. Parece que se llamó a onMeasure antes de que se onMeasure niños. Así que se me ocurrió una mezcla de estas 2 respuestas + mis propias soluciones:

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) { // 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); int h = view.getMeasuredHeight(); setMeasuredDimension(getMeasuredWidth(), h); //do not recalculate height anymore getLayoutParams().height = h; } } } 

Idea es permitir que ViewPager calcule las dimensiones de los niños y guarde la altura calculada de la primera página en los parámetros de diseño de ViewPager . No te olvides de configurar la altura del diseño del fragmento en wrap_content contrario, puedes obtener height = 0. He usado este:

     

Tenga en cuenta que esta solución funciona muy bien si todas sus páginas tienen la misma altura . De lo contrario, debe recalcular la altura de ViewPager según el niño activo actual. No lo necesito, pero si sugiere la solución, me gustaría actualizar la respuesta.

Si el ViewPager que está usando es un elemento secundario de ScrollView AND y tiene un PagerTitleStrip secundario PagerTitleStrip , deberá usar una pequeña modificación de las excelentes respuestas que ya se proporcionaron. Para referencia mi XML se ve así:

        

En su onMeasure , debe AGREGAR la PagerTitleStrip of PagerTitleStrip si se encuentra una. De lo contrario, su altura no se considerará en la altura más grande de todos los niños, a pesar de que ocupa más espacio.

Espero que esto ayude a alguien más. Lamento que sea un truco …

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

Para las personas que tienen este problema y la encoding de Xamarin Android en C #, esta también podría ser una solución rápida:

 pager.ChildViewAdded += (sender, e) => { e.Child.Measure ((int)MeasureSpecMode.Unspecified, (int)MeasureSpecMode.Unspecified); e.Parent.LayoutParameters.Height = e.Child.MeasuredHeight; }; 

Esto es principalmente útil si las vistas de su hijo son de la misma altura. De lo contrario, se le requerirá que almacene algún tipo de valor de “minimumHeight” sobre todos los niños con los que verifique, e incluso entonces podría no querer tener espacios vacíos visibles debajo de sus vistas de niños más pequeños.

La solución en sí misma no es suficiente para mí, pero eso se debe a que mis elementos secundarios son listViews y su MeasuredHeight no se calcula correctamente, parece.

Tengo una versión de WrapContentHeightViewPager que funcionaba correctamente antes de API 23 que cambiará el tamaño de la base de altura de la vista principal en la vista secundaria actual seleccionada.

Después de actualizar a la API 23, dejó de funcionar. Resulta que la solución anterior estaba usando getChildAt(getCurrentItem()) para obtener la vista hija actual para medir que no funcionaba. Vea la solución aquí: https://stackoverflow.com/a/16512217/1265583

A continuación, funciona con API 23:

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int height = 0; ViewPagerAdapter adapter = (ViewPagerAdapter)getAdapter(); View child = adapter.getItem(getCurrentItem()).getView(); if(child != null) { child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); height = child.getMeasuredHeight(); } heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } 

Utilizando la respuesta de Daniel López Localle , creé esta clase en Kotlin. Espero que te ahorre más tiempo

 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) }} 

En caso de que necesite ViewPager que ajuste su tamaño a cada elemento secundario , no solo al más grande, he escrito un fragmento de código que lo hace. Tenga en cuenta que no hay animación sobre ese cambio (no es necesario en mi caso)

android: bandera minHeight también es compatible.

 public class ChildWrappingAdjustableViewPager extends ViewPager { List childHeights = new ArrayList<>(getChildCount()); int minHeight = 0; int currentPos = 0; public ChildWrappingAdjustableViewPager(@NonNull Context context) { super(context); setOnPageChangeListener(); } public ChildWrappingAdjustableViewPager(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); obtainMinHeightAttribute(context, attrs); setOnPageChangeListener(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { childHeights.clear(); //calculate child views 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 < minHeight) { h = minHeight; } childHeights.add(i, h); } if (childHeights.size() - 1 >= currentPos) { heightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeights.get(currentPos), MeasureSpec.EXACTLY); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } private void obtainMinHeightAttribute(@NonNull Context context, @Nullable AttributeSet attrs) { int[] heightAttr = new int[]{android.R.attr.minHeight}; TypedArray typedArray = context.obtainStyledAttributes(attrs, heightAttr); minHeight = typedArray.getDimensionPixelOffset(0, -666); typedArray.recycle(); } private void setOnPageChangeListener() { this.addOnPageChangeListener(new SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { currentPos = position; ViewGroup.LayoutParams layoutParams = ChildWrappingAdjustableViewPager.this.getLayoutParams(); layoutParams.height = childHeights.get(position); ChildWrappingAdjustableViewPager.this.setLayoutParams(layoutParams); ChildWrappingAdjustableViewPager.this.invalidate(); } }); } } 

Tengo un escenario similar (pero más complejo). Tengo un diálogo, que contiene un ViewPager.
Una de las páginas secundarias es corta, con una altura estática.
Otra página secundaria siempre debe ser lo más alta posible.
Otra página secundaria contiene un ScrollView, y la página (y por lo tanto el diálogo completo) debe WRAP_CONTENT si los contenidos de ScrollView no necesitan la altura completa disponible para el diálogo.

Ninguna de las respuestas existentes funcionó por completo para este escenario específico. Espera, es un paseo lleno de baches.

 void setupView() { final ViewPager.SimpleOnPageChangeListener pageChangeListener = new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { currentPagePosition = position; // Update the viewPager height for the current view /* Borrowed from https://github.com/rnevet/WCViewPager/blob/master/wcviewpager/src/main/java/nevet/me/wcviewpager/WrapContentViewPager.java Gather the height of the "decor" views, since this height isn't included when measuring each page's view height. */ int decorHeight = 0; for (int i = 0; i < viewPager.getChildCount(); i++) { View child = viewPager.getChildAt(i); ViewPager.LayoutParams lp = (ViewPager.LayoutParams) child.getLayoutParams(); if (lp != null && lp.isDecor) { int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK; boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM; if (consumeVertical) { decorHeight += child.getMeasuredHeight(); } } } int newHeight = decorHeight; switch (position) { case PAGE_WITH_SHORT_AND_STATIC_CONTENT: newHeight += measureViewHeight(thePageView1); break; case PAGE_TO_FILL_PARENT: newHeight = ViewGroup.LayoutParams.MATCH_PARENT; break; case PAGE_TO_WRAP_CONTENT: // newHeight = ViewGroup.LayoutParams.WRAP_CONTENT; // Works same as MATCH_PARENT because...reasons... // newHeight += measureViewHeight(thePageView2); // Doesn't allow scrolling when sideways and height is clipped /* Only option that allows the ScrollView content to scroll fully. Just doing this might be way too tall, especially on tablets. (Will shrink it down below) */ newHeight = ViewGroup.LayoutParams.MATCH_PARENT; break; } // Update the height ViewGroup.LayoutParams layoutParams = viewPager.getLayoutParams(); layoutParams.height = newHeight; viewPager.setLayoutParams(layoutParams); if (position == PAGE_TO_WRAP_CONTENT) { // This page should wrap content // Measure height of the scrollview child View scrollViewChild = ...; // (generally this is a LinearLayout) int scrollViewChildHeight = scrollViewChild.getHeight(); // full height (even portion which can't be shown) // ^ doesn't need measureViewHeight() because... reasons... if (viewPager.getHeight() > scrollViewChildHeight) { // View pager too tall? // Wrap view pager height down to child height newHeight = scrollViewChildHeight + decorHeight; ViewGroup.LayoutParams layoutParams2 = viewPager.getLayoutParams(); layoutParams2.height = newHeight; viewPager.setLayoutParams(layoutParams2); } } // Bonus goodies :) // Show or hide the keyboard as appropriate. (Some pages have EditTexts, some don't) switch (position) { // This case takes a little bit more aggressive code than usual if (position needs keyboard shown){ showKeyboardForEditText(); } else if { hideKeyboard(); } } } }; viewPager.addOnPageChangeListener(pageChangeListener); viewPager.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // http://stackoverflow.com/a/4406090/4176104 // Do things which require the views to have their height populated here pageChangeListener.onPageSelected(currentPagePosition); // fix the height of the first page if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { viewPager.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { viewPager.getViewTreeObserver().removeGlobalOnLayoutListener(this); } } } ); } ... private void showKeyboardForEditText() { // Make the keyboard appear. getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); inputViewToFocus.requestFocus(); // http://stackoverflow.com/a/5617130/4176104 InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.toggleSoftInputFromWindow( inputViewToFocus.getApplicationWindowToken(), InputMethodManager.SHOW_IMPLICIT, 0); } ... /** * Hide the keyboard - http://stackoverflow.com/a/8785471 */ private void hideKeyboard() { InputMethodManager inputManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.hideSoftInputFromWindow(inputBibleBookStart.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } ... //https://github.com/rnevet/WCViewPager/blob/master/wcviewpager/src/main/java/nevet/me/wcviewpager/WrapContentViewPager.java private int measureViewHeight(View view) { view.measure(ViewGroup.getChildMeasureSpec(-1, -1, view.getLayoutParams().width), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); return view.getMeasuredHeight(); } 

Much thanks to @Raanan for the code to measure views and measure the decor height. I ran into problems with his library- the animation stuttered, and I think my ScrollView wouldn’t scroll when the height of the dialog was short enough to require it.

in my case adding clipToPadding solved the problem.

  

¡Aclamaciones!

I’ve found a solution that is a bit like merging of some of the solutions mentioned here.

The idea is to measure the current view of the ViewPager.

Extend ViewPager:

 class CalendarViewPager : ViewPager { constructor(context: Context) : super(context) {} constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {} override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) val adapter = adapter as MyPagerAdapter? val currentView = adapter?.currentView if (currentView != null) { currentView.measure(widthMeasureSpec, heightMeasureSpec) super.onMeasure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(currentView.measuredHeight, View.MeasureSpec.EXACTLY)) return } super.onMeasure(widthMeasureSpec, heightMeasureSpec) } } 

In the ViewPager adapter:

 var currentView: View? = null override fun setPrimaryItem(container: ViewGroup, position: Int, obj: Any) { super.setPrimaryItem(container, position, obj) currentView = // get the view out of the obj , depending on your code } 

If you use RecyclerPagerAdapter library , the way to get the “currentView” is by getting it from the pager-view-holder that you’ve set:

  val item = obj as PagerViewHolder currentView = item.itemView