Android: Ich kann ViewPager WRAP_CONTENT nicht haben

Ich habe einen einfachen ViewPager eingerichtet, der auf jeder Seite ein ImageView mit einer Höhe von 200 dpi hat.

Hier ist mein Pager:

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

Trotz der als wrap_content festgelegten Höhe füllt der Pager den Bildschirm immer aus, obwohl die Bildansicht nur 200dp beträgt. Ich habe versucht, die Höhe des Pagers durch “200” zu ersetzen, aber das gibt mir unterschiedliche Ergebnisse mit mehreren Auflösungen. Ich kann zu diesem Wert kein “dp” hinzufügen. Wie füge ich 200dp dem Pager-Layout hinzu?

Wenn Sie ViewPager Ihres ViewPager wie folgt ViewPager erhält es die Höhe des größten untergeordneten ViewPager , das es derzeit hat.

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

Eine weitere generische Lösung ist, dass wrap_content funktioniert.

Ich habe ViewPager erweitert, um onMeasure() zu überschreiben. Die Höhe wird um die erste untergeordnete Ansicht gelegt. Dies kann zu unerwarteten Ergebnissen führen, wenn die untergeordneten Ansichten nicht genau die gleiche Höhe haben. Dazu kann die class einfach erweitert werden, um beispielsweise auf die Größe der aktuellen Ansicht / Seite zu animieren. Aber ich brauchte das nicht.

Sie können diesen ViewPager in Ihren XML-Layouts genauso verwenden wie den ursprünglichen ViewPager:

  

Vorteil: Dieser Ansatz ermöglicht die Verwendung des ViewPagers in jedem Layout, einschließlich RelativeLayout, um andere ui-Elemente zu überlagern.

Ein Nachteil bleibt: Wenn Sie Ränder verwenden möchten, müssen Sie zwei verschachtelte Layouts erstellen und dem inneren die gewünschten Ränder geben.

Hier ist der Code:

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

Ich habe meine Antwort auf Daniel López Lacalle und diesen Beitrag basiert http://www.henning.ms/2013/09/09/viewpager-that-simply-dont-measure-up/ . Das Problem mit Daniels Antwort ist, dass meine Kinder in einigen Fällen eine Größe von Null hatten. Die Lösung war leider zweimal zu messen.

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

Auf diese Weise können Sie auch eine Höhe im ViewPager festlegen, wenn Sie dies möchten oder einfach nur wrap_content.

Ich habe gerade eine sehr ähnliche Frage zu diesem Thema beantwortet und zufällig gefunden, als ich nach einem Link gesucht habe, um meine Behauptungen zu bestätigen.

Meine andere Antwort:
Der ViewPager unterstützt wrap_content da er (normalerweise) nie alle untergeordneten wrap_content zur gleichen Zeit wrap_content und daher keine geeignete Größe erhält (die Option wäre, einen Pager zu haben, der die Größe jedes Mal ändert, wenn Sie die Seite wechseln).

Sie können jedoch eine genaue Dimension (zB 150dp) match_parent und match_parent funktioniert auch.
Sie können die Dimensionen auch dynamisch aus Ihrem Code ändern, indem Sie das Höhenattribut in seinen LayoutParams .

Für Ihre Bedürfnisse können Sie den ViewPager in einer eigenen XML-Datei erstellen, wobei die layout_height auf 200dp eingestellt ist, und dann in Ihrem Code, statt einen neuen ViewPager von Grund auf neu zu erstellen, können Sie diese xml-Datei aufblasen:

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

Ich habe dieses Problem bereits in mehreren Projekten gesehen und hatte nie eine vollständige Lösung. Also habe ich ein Github-Projekt WrapContentViewPager als Ersatz für ViewPager erstellt.

https://github.com/rnevet/WCViewPager

Die Lösung wurde von einigen Antworten hier inspiriert, verbessert sich aber:

  • Ändert dynamisch die Höhe des ViewPagers gemäß der aktuellen Ansicht, einschließlich beim Scrollen.
  • Berücksichtigt die Höhe von Dekoransichten wie PagerTabStrip.
  • Berücksichtigt alle Padding.

Aktualisiert für die Support-Bibliothek Version 24, die die vorherige Implementierung durchbrochen hat.

Ich bin gerade auf dasselbe Problem gestoßen. Ich hatte einen ViewPager und wollte auf dem Button eine Anzeige schalten. Die Lösung, die ich gefunden habe, war, den Pager in eine RelativeView zu bringen und seine layout_oben auf die View-ID zu setzen, die ich darunter sehen möchte. das hat für mich funktioniert.

Hier ist mein Layout-XML:

        

Eine andere Lösung besteht ViewPager Höhe von ViewPager entsprechend der aktuellen Seitenhöhe in PagerAdapter . Angenommen, Sie erstellen Ihre ViewPager Seiten folgendermaßen:

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

Bei mPages handelt es sich um eine interne Liste von PageInfo Strukturen, die dem PagerAdapter dynamisch hinzugefügt werden, und CustomImageView ist eine normale ImageView Methode mit überschriebenen onMeasure() -Methoden, die ihre Höhe entsprechend der angegebenen Breite festlegt und das Bildseitenverhältnis onMeasure() .

Sie können ViewPager height in der Methode setPrimaryItem() erzwingen:

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

Beachten Sie die Math.max(height, 1) . Das behebt den ärgerlichen Fehler, dass ViewPager die angezeigte Seite nicht aktualisiert (es wird leer angezeigt), wenn die vorherige Seite eine Höhe von Null hat (dh Null in der CustomImageView ), und jeder einzelne zwischen zwei Seiten hin und her CustomImageView .

Ich stieß auch auf dieses Problem, aber in meinem Fall hatte ich einen FragmentPagerAdapter , der den ViewPager mit seinen Seiten versorgte. Das Problem, das ich hatte, war, dass onMeasure() des ViewPager aufgerufen wurde, bevor eines der Fragments erstellt wurde (und sich daher nicht korrekt selbst ViewPager konnte).

Nach ein wenig Trial and Error habe ich festgestellt, dass die Methode finishUpdate() des FragmentPagerAdapter aufgerufen wird, nachdem die Fragments initialisiert wurden (aus instantiateItem() im FragmentPagerAdapter ), und auch nach / während des Blätterns auf der Seite. Ich habe eine kleine Schnittstelle gemacht:

 public interface AdapterFinishUpdateCallbacks { void onFinishUpdate(); } 

die ich an meinen FragmentPagerAdapter und anrufe:

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

was wiederum erlaubt, setVariableHeight() auf meiner CustomViewPager Implementierung 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; } } 

Ich bin mir nicht sicher, ob es der beste Ansatz ist, würde gerne Kommentare hören, wenn Sie denken, dass es gut / böse / böse ist, aber es scheint ziemlich gut in meiner Implementierung zu funktionieren 🙂

Hoffe, das hilft jemandem da draußen!

EDIT: Ich habe vergessen, eine requestLayout() nach dem Aufruf von super.measure() hinzufügen (sonst wird die Ansicht nicht neu super.measure() ).

Ich habe auch vergessen, die Polsterung der Eltern auf die endgültige Höhe hinzuzufügen.

Ich ließ auch die ursprünglichen Breite / Höhe MeasureSpecs zugunsten der Schaffung eines neuen wie erforderlich fallen. Habe den Code entsprechend aktualisiert.

Ein anderes Problem, das ich hatte, war, dass es sich in einer ScrollView nicht korrekt ScrollView und dass der Täter das Kind mit MeasureSpec.EXACTLY anstelle von MeasureSpec.UNSPECIFIED . Aktualisiert, um dies zu berücksichtigen.

Diese Änderungen wurden alle dem Code hinzugefügt. Sie können den Verlauf überprüfen, um die alten (falschen) Versionen anzuzeigen, wenn Sie möchten.

Wenn Sie statischen Inhalt im Viewpager verwenden und keine raffinierte Animation wünschen, können Sie den folgenden View Pager verwenden

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

Ich stieß auf das gleiche Problem, und ich musste auch den ViewPager um seinen Inhalt wickeln, wenn der Benutzer zwischen den Seiten scrollte. Mit der obigen Antwort von Cybergen definierte ich die onMeasure-Methode wie folgt:

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

Auf diese Weise legt die onMeasure-Methode die Höhe der aktuellen Seite fest, die vom ViewPager angezeigt wird.

Aus dem Quellcode von Popcorn time android app habe ich diese Lösung gefunden, die dynamisch die Größe von viewpager mit netten Animationen abhängig von der Größe des aktuellen Kindes anpasst.

https://git.popcortime.io/popcortime/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); } } 

Nichts von dem oben vorgeschlagenen funktionierte für mich. Mein Anwendungsfall ist 4 benutzerdefinierte ViewPagers in ScrollView . Der obere Teil wird basierend auf dem Seitenverhältnis gemessen und der Rest hat nur layout_height=wrap_content . Ich habe versucht, Cybergen , Daniel López Lacalle Lösungen. Keines von ihnen funktioniert vollständig für mich.

Meine Vermutung, warum Cybergen nicht auf Seite> 1 funktioniert, liegt daran, dass es die Höhe des Pagers basierend auf Seite 1 berechnet, die bei weiterem Scrollen ausgeblendet wird.

Sowohl Cybergen als auch Daniel López Lacalle Vorschläge haben seltsames Verhalten in meinem Fall: 2 von 3 sind ok geladen und 1 zufällig Höhe ist 0. Erscheint, dass onMeasure aufgerufen wurde, bevor Kinder bevölkert wurden. Also habe ich mir eine Mischung aus diesen 2 Antworten und meinen eigenen Korrekturen ausgedacht:

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

Idee ist es, dass ViewPager die Dimensionen von ViewPager berechnet und die berechnete Höhe der ersten Seite in Layoutparametern des ViewPager . Vergessen Sie nicht, die wrap_content Fragments auf wrap_content zu wrap_content ansonsten können Sie height = 0 erhalten. Ich habe dieses benutzt:

 < ?xml version="1.0" encoding="utf-8"?>    

Bitte beachten Sie, dass diese Lösung hervorragend funktioniert, wenn alle Ihre Seiten dieselbe Höhe haben . Andernfalls müssen Sie die ViewPager Höhe basierend auf dem aktuellen aktiven Kind neu berechnen. Ich brauche es nicht, aber wenn Sie die Lösung vorschlagen, würde ich gerne die Antwort aktualisieren.

Wenn der ViewPager Ihnen verwendete ViewPager ein Kind einer ScrollView UND ein PagerTitleStrip Kind hat, müssen Sie eine kleine Modifikation der bereits vorhandenen großartigen Antworten verwenden. Als Referenz sieht mein XML so aus:

        

In Ihrem onMeasure Sie die gemesseneHöhe des PagerTitleStrip falls eine gefunden wird. Andernfalls wird seine Höhe nicht in die größte Höhe aller Kinder aufgenommen, obwohl sie zusätzlichen Platz einnimmt.

Hoffe das hilft jemand anderem. Tut mir leid, dass es ein bisschen wie ein Hack ist …

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

Für Leute, die dieses Problem haben und Xamarin Android in C # programmieren, könnte dies auch eine schnelle Lösung sein:

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

Dies ist hauptsächlich nützlich, wenn Ihre Kindansichten dieselbe Höhe haben. Andernfalls müssten Sie eine Art “minimumHeight” -Wert für alle untergeordneten Elemente speichern, für die Sie eine Überprüfung vornehmen, und selbst dann möchten Sie möglicherweise keine leeren Bereiche unter Ihren kleineren untergeordneten Ansichten sichtbar haben.

Die Lösung selbst ist jedoch für mich nicht ausreichend, aber das liegt daran, dass meine untergeordneten Elemente listeViews sind und ihre MeasuredHeight nicht korrekt berechnet wird, so scheint es.

Ich habe eine Version von WrapContentHeightViewPager, die ordnungsgemäß vor API 23 funktionierte, die die Höhe der übergeordneten Ansicht in der aktuellen unterordneten Ansicht ändert, die ausgewählt wird.

Nach dem Upgrade auf API 23 hat es nicht mehr funktioniert. Es stellt sich heraus, dass die alte Lösung getChildAt(getCurrentItem()) , um die aktuelle getChildAt(getCurrentItem()) Ansicht zu ermitteln, die nicht funktioniert. Siehe Lösung hier: https://stackoverflow.com/a/16512217/1265583

Unten arbeitet mit 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); } 

Mit der Antwort von Daniel López Localle habe ich diese class in Kotlin erstellt. Hoffe es spart dir mehr Zeit

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

Falls Sie ViewPager benötigen, der seine Größe an jedes Kind anpasst , nicht nur an das größte, habe ich einen Code geschrieben, der das tut. Beachten Sie, dass es bei dieser Änderung keine Animation gibt (in meinem Fall nicht nötig)

Android: MinHeight Flag wird ebenfalls unterstützt.

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

Ich habe ein ähnliches (aber komplexeres Szenario). Ich habe einen Dialog, der einen ViewPager enthält.
Eine der untergeordneten Seiten ist kurz, mit einer statischen Höhe.
Eine andere untergeordnete Seite sollte immer so groß wie möglich sein.
Eine andere untergeordnete Seite enthält eine ScrollView, und die Seite (und damit der gesamte Dialog) sollte WRAP_CONTENT sein, wenn der ScrollView-Inhalt nicht die volle für den Dialog verfügbare Höhe benötigt.

Keine der vorhandenen Antworten funktionierte vollständig für dieses spezielle Szenario. Warte – es ist eine holprige Fahrt.

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

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

in my case adding clipToPadding solved the problem.

  

Prost!

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

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

Extend ViewPager:

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

In the ViewPager adapter:

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

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

  val item = obj as PagerViewHolder currentView = item.itemView