Frage

Ich habe eine benutzerdefinierte Ansichtsgruppe mit einem Kind ViewPager. Der Viewpager wird von a gefüttert PagerAdapter das liefert a LinearLayout zum ViewPager das hat LayoutParams von WRAP_CONTENT Sowohl in Höhe als auch in der Breite.

Die Ansicht wird korrekt angezeigt, aber wenn die child.measure() Die Methode wird auf dem ViewPager aufgerufen und gibt die tatsächlichen Abmessungen des Linearlayouts nicht zurück, sondern scheint den verbleibenden Raum zu füllen.

Irgendwelche Ideen, warum dies geschieht und wie man es ändert?

War es hilfreich?

Lösung

Ich war nicht sehr zufrieden mit der akzeptierten Antwort (noch mit der Lösung vor der Inflation-All-Views in den Kommentaren), also habe ich a zusammengestellt ViewPager Das nimmt seine Höhe vom ersten verfügbaren Kind an. Dies geschieht, indem Sie einen zweiten Messpass durchführen, sodass Sie die Größe des ersten Kindes stehlen können.

Eine bessere Lösung wäre, eine neue Klasse im Inneren zu erstellen android.support.v4.view Paket, das eine bessere Version von implementiert onMeasure (mit Zugriff auf paket-sichtbare Methoden wie populate())

Vorerst passt die Lösung unten gut zu mir.

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

Andere Tipps

Betrachten Sie die Interna der ViewPager -Klasse im Kompatibilitätsglas:

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

   ...
}

Es sieht so aus Auf dem vollen verfügbaren Bereich.

Meine Empfehlung wäre, eine statische Größe auf Ihrem Viewpager basierend auf der Größe Ihrer untergeordneten Ansichten festzulegen. Wenn dies unmöglich ist (zum Beispiel können die untergeordneten Ansichten variieren), müssen Sie entweder eine maximale Größe auswählen und mit dem zusätzlichen Platz in einigen Ansichten umgehen oder Viewpager erweitern und eine On -Messung bereitstellen, die die Kinder misst. Ein Problem, auf den Sie sich begegnen, ist, dass der View -Pager so konzipiert wurde, dass es sich nicht um die Breite variiert, wenn unterschiedliche Ansichten angezeigt werden. Daher werden Sie wahrscheinlich gezwungen sein, eine Größe auszuwählen und dabei zu bleiben

Wenn Sie sich im Instantiateem Ihres Pageadapters (Position) niederlassen:

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

kann dann die Ansicht (Seite des Adapters) mit einem Onpagechangelistener abrufen, messen und die Größe Ihres Viewpager -Elemente ändern:

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

Nach dem obigen Beispiel stellte ich fest, dass die Messung der Höhe der untergeordneten Ansichten nicht immer genaue Ergebnisse zurückgibt. Die Lösung besteht darin, die Höhe aller statischen Ansichten (im XML definierten Ansichten definiert) zu messen und dann die Höhe des Fragments hinzuzufügen, der unten dynamisch erzeugt wird. In meinem Fall war das statische Element der PagertitLestrip, den ich auch überschreiben musste, um die Verwendung von match_parent für die Breite im Landschaftsmodus zu ermöglichen.

Hier ist meine Einstellung zu dem Code von 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();
}}

Und die benutzerdefinierte pagertitlostrip:

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

Prost!

Mit Referenz der oben genannten Lösungen wurde eine weitere Aussage hinzugefügt, um eine maximale Sichthöhe zu erhalten.

Siehe den folgenden Code.

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

besser verändern

height = firstChild.getMeasuredHeight();

zu

height = firstChild.getMeasuredHeight() + getPaddingTop() + getPaddingBottom();
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top