Android: no puedo tener ViewPager WRAP_CONTENT

258

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

Adán
fuente
1
marque el
Cristo

Respuestas:

408

Anular onMeasure de su ViewPagersiguiente manera hará que tenga 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);
}
Daniel López Lacalle
fuente
24
Esto se acerca más a lo que necesito, pero hay dos cosas que agregar: 1. ViewPager solo cambia el tamaño al mayor de sus hijos reales, es decir, solo el elemento actualmente visible y los elementos directamente adyacentes. Llamar a setOffscreenPageLimit (número total de hijos) en ViewPager resuelve esto y da como resultado un ViewPager cuyo tamaño se establece en el mayor de todos sus elementos y nunca cambia de tamaño. 2. WebViews tiene algunos problemas extraños al intentar medirlos. Llamar a requestLayout () en un WebView después de cargar algo resuelve eso.
0101100101
3
Solo hay un pequeño problema que voy a solucionar: si viewPager tiene visibilidad para GONE y lo configura en visible, se llama a onMeasure antes de que se cree su fragmento. Por lo tanto, terminará teniendo una altura de 0. Si alguien tiene una idea, será bienvenido. Creo que iré con una devolución de llamada para cuando se cree el fragmento
edoardotognoni
44
Esto no funcionará si tiene vistas secundarias decorativas; esto se debe a que ViewPager.onMeasure () mide las vistas decorativas y les asigna primero el espacio, luego les da el resto del espacio a los elementos secundarios no decorativos. Sin embargo, esta es la solución menos incorrecta aquí, así que he votado;)
Benjamin Dobell
3
Insisto una vez más a esto cada vez que uso un ViewPager
ono
77
¡getChildCount () puede devolver 0 mientras ya ha realizado setAdapter () en ViewPager! La llamada populate () real (que crea las vistas) ocurre dentro de la super.onMeasure (widthMeasureSpec, heightMeasureSpec); llamada. Poner la llamada extra super.onMeasure () al comienzo de esta función hizo el truco. También consulte stackoverflow.com/questions/38492210/…
southerton el
106

Otra solución más genérica es llegar wrap_contental trabajo.

Me he extendido ViewPagerpara anular onMeasure(). La altura se envuelve alrededor de la primera vista secundaria. Esto podría generar resultados inesperados si las vistas secundarias no tienen exactamente la misma altura. Para eso, la clase se puede ampliar fácilmente para animar, por ejemplo, el 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.

Queda un inconveniente: si desea usar márgenes, debe crear dos diseños anidados y darle 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;
    }

}
cibergen
fuente
34
¿Alguien más recibió una página en blanco al lado del elemento actual cuando el visor se destruyó y se abrió nuevamente?
Zyoo
1
También tengo páginas en blanco.
aeren
10
Solo necesita combinar dos respuestas principales de esta pregunta como se describe en mi blog: pristalovpavel.wordpress.com/2014/12/26/…
anil
44
Simplemente reemplace el código del método 'onMeasure' con la respuesta dada por 'Daniel López Lacalle'.
Yog Guru
1
Excelente..! Trabajó para mí ... @cybergen Muchas gracias me salvaste el día ...!
Dnyanesh M
59

Basé mi respuesta en Daniel López Lacalle y 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. Desafortunadamente, la solución era 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 envolver_contenido.

MinceMan
fuente
Tuve el mismo problema y lo resolví con tu respuesta, gracias. ¿Pero alguna explicación de por qué?
Bart Burg
Creo que no tenían la intención de admitir el contenido de envoltura, ya que no creo que pensaran que era un caso de uso normal. Para apoyarlo, tenemos que volver a medirnos a nosotros mismos después de que se mida a nuestros hijos para que podamos envolver el contenido.
MinceMan
¿Por qué las imágenes de este ViewPager podía comprender más cortos que los de una ImageView que utiliza la misma scaleTypey de manera similar, layout_width=match_parental igual que layout_height=wrap_content? hay como 20dp faltando allí.
Tiburón
Tiburón, realmente no estoy seguro. Eso podría tener algo que ver con lo que realmente está haciendo su tipo de escala. Es posible que desee intentar establecer una altura.
MinceMan
1
¡NO PUEDO FRIKIN CREERLO! Pasé 2 días pegando mi visor personalizado y me quedé atrapado en un problema, cuando mi vista inicial no se mostraba y simplemente no podía entender por qué. // super has to be called in the beginning so the child views can be initialized.<----- Esa fue la razón, tuve que llamarlo al inicio y al final de la función onMeasure. ¡Yippiii, virtual choca los cinco hoy!
Starwave
37

Estaba respondiendo una pregunta muy similar sobre esto, y encontré esto cuando busqué un enlace para respaldar mis afirmaciones, por suerte :)

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

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

Para sus necesidades , puede crear ViewPager en su propio archivo xml, con el 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);
Jave
fuente
3
Buena respuesta, algo molesto porque el comportamiento predeterminado es "hacer algo algo incomprensible". Gracias por la explicación.
Chris Vandevelde
8
@ChrisVandevelde esto parece ser un inquilino común de algunas bibliotecas de Android. Tan pronto como aprende los fundamentos, se da cuenta de que nada los sigue
CQM
1
Pero @Jave, ¿por qué el visor no puede ajustar su altura cada vez que se cargan sus hijos?
Diffy
@CQM de hecho! La biblioteca ViewPagerIndicator tiene el mismo problema con layout_heightset to wrap_content, pero es aún peor ya que la solución simple para configurarlo en una cantidad fija no funciona.
Giulio Piancastelli
20

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)
}}
Felipe Castilhos
fuente
16

Ya me he enfrentado a este problema en varios proyectos y nunca tuve una solución completa. Así que creé un proyecto 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 en:

  • Cambia dinámicamente la altura de ViewPager de acuerdo con la vista actual, incluido el desplazamiento.
  • Tiene en cuenta la altura de las vistas de "decoración" como PagerTabStrip.
  • Toma en consideración todo el acolchado.

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

Raanan
fuente
@mvai, ¿puede abrir un problema o bifurcarlo y modificar la aplicación de muestra?
Raanan
1
Descubrí que RecyclerView también tiene algunos problemas de wrap_content; funciona si usa un LinearLayoutManager personalizado, como este . Así que no hay nada malo con tu biblioteca.
natario
1
Lo que aún debe corregirse es su uso con FragmentStatePagerAdapter. Parece que está midiendo niños antes de que se coloquen los fragmentos, lo que da una altura más pequeña. Lo que funcionó para mí fue la respuesta de @logan, aunque todavía estoy trabajando en ello. Es posible que desee intentar fusionar ese enfoque en su biblioteca. No estoy familiarizado con Github, lo siento.
natario
Gracias, lo examinaré.
Raanan
1
Para cualquiera que se pregunte cómo hacer que esto funcione con un FragmentPagerAdapter, haga que su Adaptador implemente ObjectAtPositionInterface manteniendo una lista de Fragmentos internamente, para que pueda devolver el Fragmento correspondiente del método getObjectAtPosition.
Pablo
15

Me encontré 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 localizador en un RelativeView y establecer su layout_ave por encima de la ID de la vista que quiero ver debajo. Eso funcionó para mí.

Aquí está mi XML de diseño:

  <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>
Idan
fuente
44
solo como referencia, no necesita xmlns: android = " schemas.android.com/apk/res/android " en ambos, solo en el primero.
Martin Marconcini
2
Su problema no fue el mismo en absoluto. Su diseño funciona bien con ViewPager configurado en match_parent: el OP tuvo una situación en la que quería que ViewPager se ajustara a su contenido.
k2col
9

También me encontré con este problema, pero en mi caso tenía una FragmentPagerAdapterque estaba suministrando ViewPagersus páginas. El problema que tuve fue que onMeasure()de la ViewPagerque se llamó antes de cualquiera de los Fragmentshubiese sido creado (y por lo tanto no podía tamaño sí correctamente).

Después de un poco de prueba y error, descubrí que el finishUpdate()método del FragmentPagerAdapter se llama después de Fragmentshaber sido inicializado (desde instantiateItem()adentro 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 FragmentPagerAdaptery llamo:

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

    if (this.listener != null)
    {
        this.listener.onFinishUpdate();
    }
}

que a su vez me permite invocar setVariableHeight()mi CustomViewPagerimplementación:

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 crees 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 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 dimensionaba correctamente en una ScrollViewy descubrí que el culpable estaba midiendo al niño en MeasureSpec.EXACTLYlugar de hacerlo MeasureSpec.UNSPECIFIED. Actualizado para reflejar esto.

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

Logan
fuente
¿Por qué no agrega al código aquellos que olvidó?
hasan
@hasan ya lo hice, perdón por cualquier confusión! Actualizaremos la respuesta para decir eso también
Logan
¡Increíble! Me alegro de haber ayudado :)
Logan
8

Otra solución es actualizar la ViewPageraltura de acuerdo con la altura de la página actual en su PagerAdapter. Asumiendo que estás creando tus ViewPagerpáginas de 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;
}

Where mPageses una lista interna de PageInfoestructuras que se agrega dinámicamente al PagerAdaptery CustomImageViewes regular ImageViewcon el onMeasure()método de reemplazo que establece su altura de acuerdo con el ancho especificado y mantiene la relación de aspecto de la imagen.

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

@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 un molesto error que ViewPagerno actualiza la página mostrada (la muestra en blanco), cuando la página anterior tiene una altura cero (es decir, nula dibujable en el CustomImageView), cada deslizamiento extraño de ida y vuelta entre dos páginas.

Blackhex
fuente
me parece el camino correcto a seguir, pero necesitaba agregar un anuncio item.mImageView.measure(..)para obtener las dimensiones correctas en los getMeasuredXXX()métodos.
Gianluca P.
6

Cuando use contenido estático dentro del visor y no desee una animación elegante, puede usar el siguiente visor

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));
  }
}
Kirill Kulakov
fuente
Esto funciona bien Lo extendí pasando por los niños y tomando el que tenía la altura máxima.
Javier Mendonça
Funciona bien incluso bajo la vista de reciclador
kanudo
Recibo esta excepción: java.lang.NullPointerException: intento de invocar el método virtual 'void android.view.View.measure (int, int)' en una referencia de objeto nulo
PJ2104
Pero tomar el primer elemento podría ser el incorrecto.
Tobias Reich
4
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);
}
Hasanul Hakim
fuente
4

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 visor con una agradable animación 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);
    }
}
Vihaan Verma
fuente
4

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

android: minHeight flag 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();
            }
        });
    }
}
Phatee P
fuente
Por lo que este adaptador tiene un gran problema cuando la cantidad que de las partidas en los cambios de adaptador
jobbert
¿Puedes aclarar tu afirmación?
Phatee P
Este código puede causar puntos nulos, ya que no todos los elementos secundarios se calculan al inicio. Pruebe un diseño de pestaña y desplácese de 1 a 5 o codifique y lo verá.
jobbert
4

Respuesta mejorada de Daniel López Lacalle , reescrita en Kotlin :

class MyViewPager(context: Context, attrs: AttributeSet): ViewPager(context, attrs) {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val zeroHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)

        val maxHeight = children
            .map { it.measure(widthMeasureSpec, zeroHeight); it.measuredHeight }
            .max() ?: 0

        if (maxHeight > 0) {
            val maxHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY)
            super.onMeasure(widthMeasureSpec, maxHeightSpec)
            return
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }
}
Wojciech Kulik
fuente
3

Me encontré con el mismo problema, y ​​también tuve que hacer que 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 que muestra ViewPager.

avlacatus
fuente
Solo el contenido más alto aparece con su respuesta, el otro contenido desapareció ...
Blaze Tama
2

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

Supongo que el cybergen no funciona en la página> 1 es porque calcula la altura del buscapersonas en función de la página 1, que se oculta si se desplaza más.

Tanto las sugerencias de cybergen como las de Daniel López Lacalle tienen un comportamiento extraño en mi caso: 2 de 3 se cargan bien y 1 altura aleatoria es 0. Aparece que onMeasurese llamó antes de que se poblaran los 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;
        }
    }
}

La idea es permitir ViewPagercalcular 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 para wrap_contentque, de lo contrario, pueda 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 ViewPageraltura según el elemento secundario activo actual. No lo necesito, pero si sugiere la solución, me complacería actualizar la respuesta.

mente
fuente
¿Podrías actualizar tu respuesta después de todos esos años? Me ayudaría muchísimo
Denny
2

Para las personas que tienen este problema y codifican 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 sus hijos son de la misma altura. De lo contrario, se le pedirá que almacene algún tipo de valor de "altura mínima" sobre todos los elementos secundarios con los que verifique, e incluso entonces es posible que no desee tener espacios vacíos visibles debajo de las 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, al parecer.

compat
fuente
Esto funcionó para mí. Todas las vistas de mi hijo en el visor son de la misma altura.
Dmitry el
2

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

Después de actualizar a API 23, dejó de funcionar. Resulta que la solución anterior estaba usando getChildAt(getCurrentItem())para obtener la vista secundaria actual para medir lo que no funciona. Ver 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);
}
Howard Lin
fuente
¡¡Gracias!! He estado probando respuestas durante horas y esta es la única que funciona completamente para mí. Debe combinarse con un adaptador personalizado donde 'setPrimaryItem ()' llama a una función en el localizador que llama requestLayout()para que la altura se ajuste a medida que avanzamos de una pestaña a la siguiente. ¿Recuerdas por qué superhay que llamar dos veces? Me di cuenta de que no funcionará de otra manera.
M3RS
Funciona con API 28.
Khalid Lakhani
2

El siguiente código es lo único que me funcionó

1. Use esta clase para declarar un HeightWrappingViewPager:

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

2. Inserte el localizador de vista de ajuste de altura en su archivo xml:

<com.project.test.HeightWrappingViewPager
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</com.project.test.HeightWrappingViewPager>

3. Declare su localizador de vistas:

HeightWrappingViewPager mViewPager;
mViewPager = (HeightWrappingViewPager) itemView.findViewById(R.id.pager);
CustomAdapter adapter = new CustomAdapter(context);
mViewPager.setAdapter(adapter);
mViewPager.measure(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
Hossam Hassan
fuente
Gracias. Esto funcionó. Pero, ¿por qué el equipo de Android no puede tener esto en su base de código?
Mohanakrrishna
Esta es una de las cosas que tiene que personalizar depende de su necesidad, también introdujo google viewPager2 en este año 2019 Google I / O y es un reemplazo del antiguo ViewPager, que fue creado en 2011, implementación 'androidx.viewpager2: viewpager2 : 1.0.0-alpha04 '
Hossam Hassan
2

Edito la respuesta de cybergen para hacer que el visor cambie de altura dependiendo del elemento seleccionado. La clase es la misma que la de cybergen, pero agregué un Vector de enteros que es todas las alturas de vistas secundarias del visor y podemos acceder a él cuando cambie la página para actualizar la altura.

Esta es la clase:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;

import java.util.Vector;

public class WrapContentHeightViewPager extends ViewPager {
    private Vector<Integer> heights = new Vector<>();

    public WrapContentHeightViewPager(@NonNull Context context) {
        super(context);
    }

    public WrapContentHeightViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

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

        for(int i=0;i<getChildCount();i++) {
            View view = getChildAt(i);
            if (view != null) {
                view.measure(widthMeasureSpec, heightMeasureSpec);
                heights.add(measureHeight(heightMeasureSpec, view));
            }
        }
        setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, getChildAt(0)));
    }

    public int getHeightAt(int position){
        return heights.get(position);
    }

    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 {
            if (view != null) {
                result = view.getMeasuredHeight();
            }
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }
}

Luego, en su actividad, agregue un OnPageChangeListener

WrapContentHeightViewPager viewPager = findViewById(R.id.my_viewpager);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
     @Override
     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
     @Override
     public void onPageSelected(int position) {
         LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) viewPager.getLayoutParams();
         params.height = viewPager.getHeightAt(position);
         viewPager.setLayoutParams(params);
     }
     @Override
     public void onPageScrollStateChanged(int state) {}
});

Y aquí está xml:

<com.example.example.WrapContentHeightViewPager
    android:id="@+id/my_viewpager"
    android:fillViewport="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

Por favor corrija mi inglés si es necesario

geggiamarti
fuente
Esto tiene algunos problemas. La heightslista puede aumentar infinito.
rosuh
@rosuh ¿Cuándo se encontró con el problema? Usé esto solo en TabLayout con ViewPager, así que no estoy seguro de si funciona bien en todas partes
geggiamarti
@geggiamarti El problema es que algunas páginas se reciclarían. Y recreado cuando el usuario se desliza hacia ellos, por lo tanto measure, se llamaría varias veces. Puede aumentar la lista de alturas. Otra situación es que el usuario puede llamar requestLayout(o setLayoutParamsmétodo, al igual que lo hizo) para este viewPager manualmente, también medirá varias veces.
rosuh
1

Si el ViewPagerque está usando es hijo de un ScrollView AND y tiene un PagerTitleStriphijo, deberá usar una ligera modificación de las excelentes respuestas que ya se proporcionaron. Como 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 onMeasuretiene que AGREGAR la altura medida del PagerTitleStripsi se encuentra uno. De lo contrario, su altura no se considerará la altura más grande de todos los niños a pesar de que ocupa espacio adicional.

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);
    }
}
alexgophermix
fuente
1

La mayoría de las soluciones que veo aquí parecen estar haciendo una doble medición: primero medir las vistas secundarias y luego llamar al super.onMeasure()

Se me ocurrió una costumbre WrapContentViewPagerque es más eficiente, funciona bien con RecyclerView y Fragment

Puedes consultar la demo aquí:

github / ssynhtn / WrapContentViewPager

y el código de la clase aquí: WrapContentViewPager.java

ssynhtn
fuente
0

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 cuadro de diálogo completo) debe WRAP_CONTENT si el contenido del ScrollView no necesita la altura total disponible para el cuadro de 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://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();
}

Muchas gracias a @Raanan por el código para medir vistas y medir la altura de la decoración. Me encontré con problemas con su biblioteca, la animación tartamudeó, y creo que mi ScrollView no se desplazaría cuando la altura del diálogo fuera lo suficientemente corta como para requerirlo.

Patricio
fuente
0

en mi caso agregar clipToPaddingsolucionó el problema.

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

¡Salud!

Mario
fuente
0

Mi caso agregando Android: fillViewport = "true" resolvió el problema

Hiten Pannu
fuente
0

En mi caso, necesitaba un visor con un wrap_content para el elemento y la animación actualmente seleccionados al aplicar el tamaño. A continuación puedes ver mi implementación. ¿Alguien puede ser útil?

package one.xcorp.widget

import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import one.xcorp.widget.R
import kotlin.properties.Delegates.observable

class ViewPager : android.support.v4.view.ViewPager {

    var enableAnimation by observable(false) { _, _, enable ->
        if (enable) {
            addOnPageChangeListener(onPageChangeListener)
        } else {
            removeOnPageChangeListener(onPageChangeListener)
        }
    }

    private var animationDuration = 0L
    private var animator: ValueAnimator? = null

    constructor (context: Context) : super(context) {
        init(context, null)
    }

    constructor (context: Context, attrs: AttributeSet?) : super(context, attrs) {
        init(context, attrs)
    }

    private fun init(context: Context, attrs: AttributeSet?) {
        context.theme.obtainStyledAttributes(
            attrs,
            R.styleable.ViewPager,
            0,
            0
        ).apply {
            try {
                enableAnimation = getBoolean(
                    R.styleable.ViewPager_enableAnimation,
                    enableAnimation
                )
                animationDuration = getInteger(
                    R.styleable.ViewPager_animationDuration,
                    resources.getInteger(android.R.integer.config_shortAnimTime)
                ).toLong()
            } finally {
                recycle()
            }
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)

        val measuredHeight = if (heightMode == MeasureSpec.EXACTLY) {
            MeasureSpec.getSize(heightMeasureSpec)
        } else {
            val currentViewHeight = findViewByPosition(currentItem)?.also {
                measureView(it)
            }?.measuredHeight ?: 0

            if (heightMode != MeasureSpec.AT_MOST) {
                currentViewHeight
            } else {
                Math.min(
                    currentViewHeight,
                    MeasureSpec.getSize(heightMeasureSpec)
                )
            }
        }

        super.onMeasure(
            widthMeasureSpec,
            MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)
        )
    }

    private fun measureView(view: View) = with(view) {
        val horizontalMode: Int
        val horizontalSize: Int
        when (layoutParams.width) {
            MATCH_PARENT -> {
                horizontalMode = MeasureSpec.EXACTLY
                horizontalSize = this@ViewPager.measuredWidth
            }
            WRAP_CONTENT -> {
                horizontalMode = MeasureSpec.UNSPECIFIED
                horizontalSize = 0
            }
            else -> {
                horizontalMode = MeasureSpec.EXACTLY
                horizontalSize = layoutParams.width
            }
        }

        val verticalMode: Int
        val verticalSize: Int
        when (layoutParams.height) {
            MATCH_PARENT -> {
                verticalMode = MeasureSpec.EXACTLY
                verticalSize = this@ViewPager.measuredHeight
            }
            WRAP_CONTENT -> {
                verticalMode = MeasureSpec.UNSPECIFIED
                verticalSize = 0
            }
            else -> {
                verticalMode = MeasureSpec.EXACTLY
                verticalSize = layoutParams.height
            }
        }

        val horizontalMeasureSpec = MeasureSpec.makeMeasureSpec(horizontalSize, horizontalMode)
        val verticalMeasureSpec = MeasureSpec.makeMeasureSpec(verticalSize, verticalMode)

        measure(horizontalMeasureSpec, verticalMeasureSpec)
    }

    private fun findViewByPosition(position: Int): View? {
        for (i in 0 until childCount) {
            val childView = getChildAt(i)
            val childLayoutParams = childView.layoutParams as LayoutParams

            val childPosition by lazy {
                val field = childLayoutParams.javaClass.getDeclaredField("position")
                field.isAccessible = true
                field.get(childLayoutParams) as Int
            }

            if (!childLayoutParams.isDecor && position == childPosition) {
                return childView
            }
        }

        return null
    }

    private fun animateContentHeight(childView: View, fromHeight: Int, toHeight: Int) {
        animator?.cancel()

        if (fromHeight == toHeight) {
            return
        }

        animator = ValueAnimator.ofInt(fromHeight, toHeight).apply {
            addUpdateListener {
                measureView(childView)
                if (childView.measuredHeight != toHeight) {
                    animateContentHeight(childView, height, childView.measuredHeight)
                } else {
                    layoutParams.height = animatedValue as Int
                    requestLayout()
                }
            }
            duration = animationDuration
            start()
        }
    }

    private val onPageChangeListener = object : OnPageChangeListener {

        override fun onPageScrollStateChanged(state: Int) {
            /* do nothing */
        }

        override fun onPageScrolled(
            position: Int,
            positionOffset: Float,
            positionOffsetPixels: Int
        ) {
            /* do nothing */
        }

        override fun onPageSelected(position: Int) {
            if (!isAttachedToWindow) {
                return
            }

            findViewByPosition(position)?.let { childView ->
                measureView(childView)
                animateContentHeight(childView, height, childView.measuredHeight)
            }
        }
    }
}

Agregue attrs.xml en el proyecto:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ViewPager">
        <attr name="enableAnimation" format="boolean" />
        <attr name="animationDuration" format="integer" />
    </declare-styleable>
</resources>

Y use:

<one.xcorp.widget.ViewPager
    android:id="@+id/wt_content"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:enableAnimation="true" />
maXp
fuente
0

Este ViewPager solo cambia el tamaño a los hijos visibles actuales (no el mayor de sus hijos reales)

La idea de https://stackoverflow.com/a/56325869/4718406

public class DynamicHeightViewPager extends ViewPager {

public DynamicHeightViewPager (Context context) {
    super(context);
    initPageChangeListener();
}

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



private void initPageChangeListener() {
    addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            requestLayout();
        }
    });
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //View child = getChildAt(getCurrentItem());
    View child = getCurrentView(this);
    if (child != null) {
        child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, 
         MeasureSpec.UNSPECIFIED));
        int h = child.getMeasuredHeight();

        heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}


View getCurrentView(ViewPager viewPager) {
    try {
        final int currentItem = viewPager.getCurrentItem();
        for (int i = 0; i < viewPager.getChildCount(); i++) {
            final View child = viewPager.getChildAt(i);
            final ViewPager.LayoutParams layoutParams = (ViewPager.LayoutParams) 
             child.getLayoutParams();

            Field f = layoutParams.getClass().getDeclaredField("position"); 
            //NoSuchFieldException
            f.setAccessible(true);
            int position = (Integer) f.get(layoutParams); //IllegalAccessException

            if (!layoutParams.isDecor && currentItem == position) {
                return child;
            }
        }
    } catch (NoSuchFieldException e) {
        e.fillInStackTrace();
    } catch (IllegalArgumentException e) {
        e.fillInStackTrace();
    } catch (IllegalAccessException e) {
        e.fillInStackTrace();
    }
    return null;
}

}

Erfan Eghterafi
fuente
0

Mida la altura de 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;
    }

}

conjunto de llamadasPrimaryView (Ver) :

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

}
wslaimin
fuente
0

Dar diseño principal de ViewPager como NestedScrollView

   <androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    android:fillViewport="true">
        <androidx.viewpager.widget.ViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </androidx.viewpager.widget.ViewPager>
    </androidx.core.widget.NestedScrollView>

No te olvides de configurar android:fillViewport="true"

Esto estirará la vista de desplazamiento y el contenido de su hijo para llenar la ventana gráfica.

https://developer.android.com/reference/android/widget/ScrollView.html#attr_android:fillViewport

Krishna
fuente
0

Puede cambiar a ViewPager2. Es una versión actualizada de ViewPager. Hace lo mismo que ViewPager pero de una manera más inteligente y eficiente. ViewPager2 viene con una variedad de nuevas características. Por supuesto, ViewPager2 ha resuelto el problema de envoltura de contenido.

De los documentos de Android: "ViewPager2 reemplaza a ViewPager, abordando la mayoría de los puntos débiles de su predecesor, incluido el soporte de diseño de derecha a izquierda, orientación vertical, colecciones de fragmentos modificables, etc."

Recomiendo este artículo para principiantes:

https://medium.com/google-developer-experts/exploring-the-view-pager-2-86dbce06ff71

seyfullah.bilgin
fuente
Este problema todavía está allí, consulte issuetracker.google.com/u/0/issues/143095219
Somesh Kumar