versiones pie oreo developer descargar android

pie - Android: no puedo tener ViewPager WRAP_CONTENT



android pie (22)

He configurado 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 paginador siempre llena la pantalla aunque la vista de la imagen sea de solo 200dp. Intenté reemplazar la altura del buscapersonas con "200", pero eso me da diferentes resultados con varias resoluciones. No puedo agregar "dp" a ese valor. ¿Cómo agrego 200dp al diseño del localizador?


Al anular la Medición de su ViewPager siguiente manera, obtendrá la altura del niño 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); }


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 lamentablemente 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 el ViewPager si así lo desea o simplemente wrap_content.


Cuando use contenido estático dentro del viewpager y no desea una animación elegante, puede usar el siguiente paginador de vista

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


Desde el código fuente de la aplicación de Android Popcorn time, encontré esta solución que ajusta dinámicamente el tamaño del viewpager con una animación agradable dependiendo del tamaño del niño 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); } }


En caso de que necesite ViewPager que ajuste su tamaño a cada niño , 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<Integer> 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(); } }); } }


Me topé con el mismo problema, y ​​también tuve que hacer que el ViewPager se ajustara a su contenido 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 manera, el método onMeasure establece la altura de la página actual mostrada por ViewPager.


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 establecer su layout_above en el id de vista que quiero ver debajo. eso funciono para mi

Aquí está mi diseño 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>


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 resto solo tiene layout_height=wrap_content . He intentado cybergen , soluciones de Daniel López Lacalle . Ninguno de ellos trabaja completamente para mí.

Mi suposición de por qué cybergen no funciona en la página> 1 es porque calcula la altura del paginador según la página 1, que está oculta si se desplaza más.

Tanto el cybergen como las sugerencias de Daniel López Lacalle tienen un comportamiento extraño en mi caso: 2 de 3 están bien cargadas y 1 al azar es 0. Aparece que se llamó a onMeasure antes de que se poblaran los niños. Entonces se me ocurrió una mezcla de estas 2 respuestas + mis propios arreglos:

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

La idea es permitir que ViewPager calcule las dimensiones de los niños y guardar la altura calculada de la primera página en los parámetros de diseño del ViewPager . No olvide establecer la altura de diseño del fragmento en wrap_content contrario, puede obtener height = 0. He usado este:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- Childs are populated in fragment --> </LinearLayout>

Tenga en cuenta que esta solución funciona muy bien si todas sus páginas tienen la misma altura . De lo contrario, debe volver a calcular la altura de ViewPager según el hijo actual activo. No lo necesito, pero si sugieres la solución, estaré encantado de actualizar la respuesta.


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 agregadas dinámicamente a PagerAdapter y CustomImageView es simplemente ImageView regular con el método onMeasure() que establece su altura según 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 corrige el error molesto que ViewPager no actualiza la página mostrada (la muestra en blanco), cuando la página anterior tiene una altura cero (es decir, nula CustomImageView en la CustomImageView ), cada barrido impar entre dos páginas.


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

He extendido ViewPager para anular onMeasure() . La altura se enrolla alrededor de la vista del primer niño. Esto podría llevar a resultados inesperados si las vistas secundarias no tienen exactamente la misma altura. Para eso, la clase se puede extender fácilmente a, digamos, animar 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:

<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"/>

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

Sigue habiendo un inconveniente: si desea utilizar márgenes, debe crear dos diseños anidados y asignar al interior 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; } }


Para las personas que tienen este problema y la codificación para 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 solicitará que almacene algún tipo de valor de "altura mínima" sobre todos los elementos secundarios contra los que verifique, y aun así, es posible que no desee tener espacios vacíos visibles debajo de sus vistas secundarias más pequeñas.

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


Si el ViewPager que está utilizando es hijo de un ScrollView AND tiene un PagerTitleStrip child, deberá usar una ligera modificación de las excelentes respuestas que ya se proporcionaron. Para referencia mi XML se ve así:

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

En su onMeasure tiene que AGREGAR la PagerTitleStrip medida del PagerTitleStrip si se encuentra una. De lo contrario, su altura no se considerará en la altura más grande de todos los niños aunque ocupe espacio adicional.

Espero que esto ayude a alguien más. Lo siento que es un poco de un 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); } }


Solo estaba respondiendo una pregunta muy similar sobre esto, y encontré esto cuando buscaba un enlace para respaldar mis afirmaciones, así que afortunadamente :)

Mi otra respuesta:
El 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 localizador que cambie de tamaño cada vez que haya cambiado de página).

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

Para sus necesidades , puede crear el 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);


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

Después de un poco de prueba y error, descubrí 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 en mi FragmentPagerAdapter y llamo:

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

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ían los comentarios si consideras 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 se vuelve a dibujar la vista).

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

También dejé de guardar las Mediciones de Ancho / altura originales para crear una nueva según sea necesario. Han actualizado el código en consecuencia.

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

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


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


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

https://github.com/rnevet/WCViewPager

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

  • Cambia dinámicamente la altura del ViewPager de acuerdo con la vista actual, incluido el desplazamiento.
  • 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.


He encontrado una solución que es un poco como la fusión de algunas de las soluciones mencionadas aquí.

La idea es medir la vista actual del ViewPager.

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

En el adaptador ViewPager:

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 }

Si usa la biblioteca RecyclerPagerAdapter , la forma de obtener "currentView" es obteniéndola del titular de la vista de paginador que ha establecido:

val item = obj as PagerViewHolder currentView = item.itemView


Mi caso al agregar android: fillViewport = "true" solucionó el problema


Tengo una versión de WrapContentHeightViewPager que funcionaba correctamente antes de API 23 que cambiará el tamaño de la base 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 se estaba usando getChildAt(getCurrentItem())para obtener la vista secundaria actual para medir lo que no funciona. Vea la solución aquí: https://.com/a/16512217/1265583

A continuación trabaja 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); }


En mi caso añadiendo clipToPaddingsolucionado el problema.

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

¡Aclamaciones!


Tengo un escenario similar (pero más complejo). Tengo un cuadro de 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 el contenido de ScrollView no necesita la altura completa disponible para el diálogo.

Ninguna de las respuestas existentes funcionó completamente para este escenario específico. Espera, es un viaje 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://.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://.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://.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ísimas gracias a @Raanan por el código para medir vistas y medir la altura de la decoración. Me topé con problemas con su biblioteca: la animación tartamudeaba, y creo que mi ScrollView no se desplazaría cuando la altura del diálogo fuera lo suficientemente corta como para requerirlo.


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