Domanda

Ho un ViewGroup personalizzato che ha un ViewPager bambino. Il ViewPager è alimentato da una PagerAdapter che fornisce un LinearLayout al ViewPager che ha LayoutParams di WRAP_CONTENT su altezza e larghezza.

La vista viene visualizzata correttamente ma quando il metodo viene chiamato child.measure() sul ViewPager non restituisce le dimensioni effettive del LinearLayout ma sembra riempire tutto lo spazio rimanente.

Tutte le idee perché questo sta accadendo e come modificarlo?

È stato utile?

Soluzione

Non era molto felice con la risposta accettata (né con la soluzione pre gonfiare-all-viste nei commenti), così ho messo insieme un ViewPager che prende la sua altezza dal primo figlio a disposizione. Lo fa fare un secondo passaggio di misura, che consente di rubare l'altezza del primo bambino.

Una migliore soluzione sarebbe quella di effettuare una nuova classe all'interno dell'imballaggio android.support.v4.view che implementa una versione migliore di onMeasure (con accesso a metodi del pacchetto visibile come populate())

Per il momento, però, la soluzione qui di seguito tute me bene.

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

        boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) 
                == MeasureSpec.AT_MOST;

        if(wrapHeight) {
            /**
             * The first super.onMeasure call made the pager take up all the 
             * available height. Since we really wanted to wrap it, we need 
             * to remeasure it. Luckily, after that call the first child is 
             * now available. So, we take the height from it. 
             */

            int width = getMeasuredWidth(), height = getMeasuredHeight();

            // Use the previously measured width but simplify the calculations
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);

            /* If the pager actually has any children, take the first child's 
             * height and call that our own */ 
            if(getChildCount() > 0) {
                View firstChild = getChildAt(0);

                /* The child was previously measured with exactly the full height.
                 * Allow it to wrap this time around. */
                firstChild.measure(widthMeasureSpec, 
                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));

                height = firstChild.getMeasuredHeight();
            }

            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}

Altri suggerimenti

Guardando le interni della classe ViewPager nel vaso di compatibilità:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    // For simple implementation, or internal size is always 0.
    // We depend on the container to specify the layout size of
    // our view. We can't really know what it is since we will be
    // adding and removing different arbitrary views and do not
    // want the layout to change as this happens.
    setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));

   ...
}

Sembrerebbe che l'attuazione ViewPager non misura il punto di vista dei bambini, ma solo imposta il ViewPager essere una visualizzazione standard in base a ciò che il genitore sta passando. Quando si passa wrap_content, dal momento che il cercapersone vista in realtà non misurare la sua contenuti che occupa l'intera area a disposizione.

Il mio consiglio sarebbe quello di impostare una dimensione statica sul tuo ViewPager in base alle dimensioni del vostro punto di vista del bambino. Se questo è impossibile (per esempio, il punto di vista del bambino possono variare) potrete sia bisogno di scegliere una dimensione massima e trattare con lo spazio in più in alcuni punti di vista o estendere ViewPager e fornire un onMeasure che misura i bambini. Un problema che verrà eseguito in è che il pager vista è stato progettato per non variano in larghezza sono mostrati diversi punti di vista, in modo probabilmente sarete costretti a scegliere una dimensione e soggiorno con esso

Se setTag (posizione) nel instantiateItem del vostro PageAdapter:

@Override
public Object instantiateItem(ViewGroup collection, int page) {
    LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View view = (View) inflater.inflate(R.layout.page_item , null);
    view.setTag(page);

quindi in grado di recuperare la vista (pagina della adattatore) con un'OnPageChangeListener, misurarla, e ridimensionare il vostro ViewPager:

private ViewPager pager;
@Override
protected void onCreate(Bundle savedInstanceState) {
    pager = findViewById(R.id.viewpager);
    pager.setOnPageChangeListener(new SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            resizePager(position);
        }
    });

    public void resizePager(int position) {
        View view = pager.findViewWithTag(position);
        if (view == null) 
            return;
        view.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        int width = view.getMeasuredWidth();
        int height = view.getMeasuredHeight();
            //The layout params must match the parent of the ViewPager 
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width , height); 
        pager.setLayoutParams(params);
    }
}

Seguendo l'esempio di cui sopra ho scoperto che misurare l'altezza dei punti di vista del bambino non restituisce sempre risultati accurati. La soluzione è quello di misurare l'altezza di eventuali viste statiche (definito in XML) e quindi aggiungere l'altezza del frammento che si crea dinamicamente in basso. Nel mio caso l'elemento statico è stato il PagerTitleStrip, che ho avuto anche a override in modo da consentire l'utilizzo di match_parent per la larghezza in modalità orizzontale.

Così qui è il mio prendere sul codice da Delyan:

public class WrappingViewPager extends ViewPager {

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

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // super has to be called in the beginning so the child views can be
    // initialized.
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (getChildCount() <= 0)
        return;

    // Check if the selected layout_height mode is set to wrap_content
    // (represented by the AT_MOST constraint).
    boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec)
            == MeasureSpec.AT_MOST;

    int width = getMeasuredWidth();

    View firstChild = getChildAt(0);

    // Initially set the height to that of the first child - the
    // PagerTitleStrip (since we always know that it won't be 0).
    int height = firstChild.getMeasuredHeight();

    if (wrapHeight) {

        // Keep the current measured width.
        widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);

    }

    int fragmentHeight = 0;
    fragmentHeight = measureFragment(((Fragment) getAdapter().instantiateItem(this, getCurrentItem())).getView());

    // Just add the height of the fragment:
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + fragmentHeight,
            MeasureSpec.EXACTLY);

    // super has to be called again so the new specs are treated as
    // exact measurements.
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

public int measureFragment(View view) {
    if (view == null)
        return 0;

    view.measure(0, 0);
    return view.getMeasuredHeight();
}}

E il PagerTitleStrip personalizzato:

public class MatchingPagerTitleStrip extends android.support.v4.view.PagerTitleStrip {

public MatchingPagerTitleStrip(Context arg0, AttributeSet arg1) {
    super(arg0, arg1);

}

@Override
protected void onMeasure(int arg0, int arg1) {

    int size = MeasureSpec.getSize(arg0);

    int newWidthSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);

    super.onMeasure(newWidthSpec, arg1);
}}

Cheers!

Con riferimento delle soluzioni di cui sopra, ha aggiunto un po 'di dichiarazione per ottenere la massima altezza del bambino vista cercapersone.

Segnala il sottostante codice.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // super has to be called in the beginning so the child views can be
    // initialized.
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (getChildCount() <= 0)
        return;

    // Check if the selected layout_height mode is set to wrap_content
    // (represented by the AT_MOST constraint).
    boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;

    int width = getMeasuredWidth();

    int childCount = getChildCount();

    int height = getChildAt(0).getMeasuredHeight();
    int fragmentHeight = 0;

    for (int index = 0; index < childCount; index++) {
        View firstChild = getChildAt(index);

        // Initially set the height to that of the first child - the
        // PagerTitleStrip (since we always know that it won't be 0).
        height = firstChild.getMeasuredHeight() > height ? firstChild.getMeasuredHeight() : height;

        int fHeight = measureFragment(((Fragment) getAdapter().instantiateItem(this, index)).getView());

        fragmentHeight = fHeight > fragmentHeight ? fHeight : fragmentHeight;

    }

    if (wrapHeight) {

        // Keep the current measured width.
        widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);

    }

    // Just add the height of the fragment:
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + fragmentHeight, MeasureSpec.EXACTLY);

    // super has to be called again so the new specs are treated as
    // exact measurements.
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

meglio i cambiamenti

height = firstChild.getMeasuredHeight();

a

height = firstChild.getMeasuredHeight() + getPaddingTop() + getPaddingBottom();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top