Android: ViewPager'e sahip olamıyorum WRAP_CONTENT


258

Her sayfada 200dp yüksekliğe sahip bir ImageView olan basit bir ViewPager kurduk.

İşte benim çağrı cihazı:

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

Wrap_content olarak ayarlanan yüksekliğe rağmen, görüntüleyici yalnızca 200 dp olsa bile çağrı cihazı ekranı her zaman doldurur. Çağrı cihazının yüksekliğini "200" ile değiştirmeye çalıştım, ancak bu bana çoklu çözünürlüklerle farklı sonuçlar veriyor. Bu değere "dp" ekleyemiyorum. Çağrı cihazının düzenine nasıl 200dp ekleyebilirim?


Yanıtlar:


408

ViewPagerAşağıdaki şekilde ölçmek, şu anda sahip olduğu en büyük çocuğun yüksekliğini elde etmesini sağlayacaktır.

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

24
Bu, ihtiyacım olana en yakın geliyor, ancak eklenecek iki şey var: 1. ViewPager yalnızca gerçek çocuklarının en büyüğüne, yani yalnızca şu anda görünen öğeye ve doğrudan bitişik olanlara yeniden boyutlandırılır. ViewPager üzerinde setOffscreenPageLimit (toplam çocuk sayısı) çağrısı bunu çözer ve boyutu tüm öğelerinin en büyüğüne ayarlanmış ve hiçbir zaman yeniden boyutlandırılmamış bir ViewPager ile sonuçlanır. 2. WebView'larda ölçmeye çalışırken bazı garip sorunlar var. Bir şey yükledikten sonra WebView'de requestLayout () öğesini çağırmak bunu çözer.
0101100101

3
Düzelteceğim sadece küçük bir sorun var: viewPager görünürlüğü GONE olarak görünürse ve görünür olarak ayarladıysanız, parçası oluşturulmadan önce onMeasure çağrılır. Bu yüzden 0 yüksekliğe sahip olacak. Eğer bir fikri varsa, hoş geldiniz. Ben parçanın yaratıldığı zaman için bir geri arama ile gideceğim düşünüyorum
edoardotognoni

4
Bu, dekor çocuk görünümleriniz varsa işe yaramaz - bunun nedeni ViewPager.onMeasure () dekor görünümlerini ölçer ve önce onlara alan ayırır, daha sonra alanın geri kalanını dekor olmayan çocuklara verir. Yine de, bu kadar burada en az yanlış çözüm bu yüzden upvoted;)
Benjamin

3
Bir ViewPager'i her kullandığımda buna geri dönüyorum
ono

7
ViewPager'de setAdapter () işlemini gerçekleştirirken getChildCount () 0 döndürebilir! Gerçek populate () çağrısı (görünümleri oluşturur) super.onMeasure (widthMeasureSpec, heightMeasureSpec) içinde gerçekleşir; aramak. Ekstra super.onMeasure () çağrısını bu işlevin başına koymak hile yaptı. Ayrıca stackoverflow.com/questions/38492210/…
southerton

106

Başka bir genel çözüm, wrap_contentsadece işe gitmektir.

ViewPagerGeçersiz kılmak için genişlettim onMeasure(). Yükseklik, ilk çocuk görünümü etrafına sarılır. Çocuk görünümleri tam olarak aynı yükseklikte değilse bu beklenmedik sonuçlara yol açabilir. Bunun için sınıf, mevcut görünümün / sayfanın boyutuna animasyon ekleyecek şekilde kolayca genişletilebilir. Ama buna ihtiyacım yoktu.

Bu ViewPager'i orijinal ViewPager gibi XML düzenlerinde kullanabilirsiniz:

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

Avantajı: Bu yaklaşım, diğer kullanıcı arabirimi öğelerini kaplamak için ViewPager'i RelativeLayout dahil herhangi bir düzende kullanmaya olanak tanır.

Bir dezavantaj kalıyor: Kenar boşluklarını kullanmak istiyorsanız, iki iç içe düzen oluşturmanız ve iç kısma istediğiniz kenar boşluklarını vermeniz gerekir.

İşte kod:

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

}

34
görüntüleyici yok edip tekrar açıldığında mevcut öğenin yanında boş sayfa var mı?
Zyoo

1
Benim de boş sayfalarım var.
aeren

10
Blogumda açıklandığı gibi bu sorunun en iyi iki cevabını birleştirmeniz gerekiyor: pristalovpavel.wordpress.com/2014/12/26/…
anil

4
'OnMeasure' yönteminin kodunu 'Daniel López Lacalle' tarafından verilen cevapla değiştirin.
Yog Guru

1
Harika..! Benim için çalıştı .. @ cybergen Çok teşekkürler u günümü kurtardı ..!
Dnyanesh M

59

Cevabımı Daniel López Lacalle ve bu gönderiye dayandım http://www.henning.ms/2013/09/09/viewpager-that-simply-dont-measure-up/ . Daniel'in cevabı ile ilgili sorun, bazı durumlarda çocuklarımın boyunun sıfır olmasıdır. Çözüm maalesef iki kez ölçülecekti.

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

Bu ayrıca, wrap_content yapmak veya yalnızca wrap_content yapmak istiyorsanız ViewPager üzerinde bir yükseklik ayarlamanıza olanak tanır.


Aynı sorunu yaşadım ve cevabınızla çözdüm, teşekkürler. Ama neden olduğuna dair herhangi bir açıklama var mı?
Bart Burg

Ben normal bir kullanım örneği olduğunu düşündüklerini sanmıyorum gibi wrap içeriği desteklenmesi niyetinde değildi düşünüyorum. Bunu desteklemek için çocuklarımızı ölçtükten sonra kendimizi ölçmek zorundayız, böylece içeriği sarabiliriz.
MinceMan

Neden bu ViewPager görüntüler aynı kullanan bir ImageView oranla daha aslında kısadır scaleType, benzer ve layout_width=match_parentaynı zamanda layout_height=wrap_content? 20dp eksik gibi.
Köpekbalığı

Köpekbalığı, gerçekten emin değilim. Bunun, ölçek türünüzün gerçekte yaptığı şeyle ilgisi olabilir. Yükseklik ayarlamayı denemek isteyebilir.
MinceMan

1
FRIKIN İNANAMIYORUM! Özel günüm kamerayı birbirine yapıştırarak 2 gün geçirdim ve ilk görüşüm görünmediğinde bir soruna takıldım ve nedenini anlayamadım! // super has to be called in the beginning so the child views can be initialized.Bunun nedeni buydu, başlangıçta ve onMeasure fonksiyonunun sonunda çağırmak zorundaydı. Yippiii, bugün üstümde yüksek beşlik!
Starwave

37

Bu konuda çok benzer bir soruyu yanıtlıyordum ve iddialarımı yedeklemek için bir bağlantı ararken bunu buldum, çok şanslısın :)

Diğer cevabım:
ViewPager desteklemiyor wrap_content(genellikle) hiçbir zaman tüm çocuklarını aynı anda yüklemiyor ve bu nedenle uygun bir boyut alamıyor (seçenek, her geçişinizde boyutu değiştiren bir çağrı cihazına sahip olmak olacaktır sayfa).

Bununla birlikte, kesin bir boyut (ör. 150dp) ayarlayabilirsiniz ve de match_parentçalışır.
Ayrıca, içindeki height-attribute değerini değiştirerek boyutları kodunuzdan dinamik olarak değiştirebilirsiniz LayoutParams.

İhtiyaçlarınız için ViewPager'i kendi xml dosyasında, layout_height 200dp olarak ayarlanmış ve daha sonra kodunuzda, sıfırdan yeni bir ViewPager oluşturmak yerine, bu xml dosyasını şişirebilirsiniz:

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

3
İyi yanıt, varsayılan davranışın "biraz anlaşılmaz bir şey yapması" gibi can sıkıcı bir durum. Açıklama için teşekkürler.
Chris Vandevelde

8
@ChrisVandevelde bu bazı android kütüphanelerinin ortak kiracısı gibi görünüyor. Temel bilgileri öğrenir öğrenmez, hiçbir şeyin onları takip etmediğini fark edersiniz
CQM

1
Ama @Jave, neden viewpager çocukları her yüklendiğinde yüksekliğini ayarlayamıyor?
Diffy

@CQM gerçekten! ViewPagerIndicator kitaplığı layout_heightayarlanmış ile aynı soruna sahiptir wrap_content, ancak sabit bir tutara ayarlamak için basit bir çözüm çalışmaz.
Giulio Piancastelli

20

Daniel López Localle cevabını kullanarak bu dersi Kotlin'de yarattım. Umarım daha fazla zaman kazandırır

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

16

Bu konuyla zaten birkaç projede karşılaştım ve hiçbir zaman tam bir çözümüm olmadı. Bu yüzden ViewPager yerine yerinde bir WrapContentViewPager github projesi oluşturdum.

https://github.com/rnevet/WCViewPager

Çözüm, buradaki bazı cevaplardan ilham aldı, ancak geliştirdi:

  • Kaydırma dahil olmak üzere ViewPager yüksekliğini geçerli Görünüme göre dinamik olarak değiştirir.
  • PagerTabStrip gibi "dekor" görünümlerinin yüksekliğini dikkate alır.
  • Tüm Dolguları dikkate alır.

Önceki uygulamayı bozan destek kitaplığı sürüm 24 için güncellendi.


@mvai bir sorunu açabilir veya çatabilir ve örnek uygulamayı değiştirebilir misiniz?
Raanan

1
RecyclerView bazı wrap_content sorunları da öğrendim; Eğer gibi özel bir LinearLayoutManager kullanırsanız çalıştığını bu . Bu yüzden kütüphanenizde yanlış bir şey yok.
natario

1
Hala düzeltilmesi gereken FragmentStatePagerAdapter ile kullanılmasıdır. Parçalar yerleştirilmeden önce çocukları ölçüyor gibi görünüyor, böylece daha küçük yükseklik veriyor. Benim için işe yarayan şey, @logan'ın cevabıydı, ancak hala üzerinde çalışıyorum. Bu yaklaşımı kitaplığınızla birleştirmeyi deneyebilirsiniz. Github'a aşina değilim, üzgünüm.
natario

Teşekkürler, buna bir göz atacağım.
Raanan

1
Bir FragmentPagerAdapter ile bu işi nasıl yapacağını merak eden herkes için, adaptörünüzün bir Parça listesini dahili olarak tutarak ObjectAtPositionInterface uygulamasını gerçekleştirmesini sağlayın, böylece getObjectAtPosition yönteminden ilgili Parçayı döndürebilir.
Pablo

15

Ben sadece aynı konuya çarptım. Bir ViewPager'im vardı ve düğmesinde bir reklam göstermek istedim. Bulduğum çözüm, çağrı cihazını bir RelativeView içine almak ve bunun layout_above değerini aşağıda görmek istediğim görünüm kimliğine ayarlamaktı. benim için işe yaradı.

İşte benim düzen XML:

  <RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:id="@+id/AdLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical" >
    </LinearLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/mainpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/AdLayout" >
    </android.support.v4.view.ViewPager>
</RelativeLayout>

4
sadece referans için, her ikisinde de sadece ilkinde xmlns: android = " schemas.android.com/apk/res/android " gerekmez .
Martin Marconcini

2
Sorununuz hiç aynı değildi. Görünümünüz ViewPager ile match_parent olarak ayarlandığında iyi çalışır - OP'nin ViewPager'in içeriğine sarmasını istediği bir durum vardı.
k2col

9

Ben de bu sorunla karşılaştım, ama benim durumumda onun sayfaları ile FragmentPagerAdaptertedarik bir vardı ViewPager. Ben sorun olmasıydı onMeasure()arasında ViewPagerherhangi önce çağrıldı Fragmentsoluşturulan (ve dolayısıyla yapamadı boyut kendisi doğru) olmuştu.

Biraz deneme yanılma finishUpdate()işleminden sonra, FragmentPagerAdapter yönteminin Fragmentsbaşlatıldıktan sonra ( instantiateItem()içinde FragmentPagerAdapter) ve ayrıca sayfa kaydırma sırasında / sırasında çağrıldığını gördüm . Küçük bir arayüz yaptım:

public interface AdapterFinishUpdateCallbacks
{
    void onFinishUpdate();
}

hangi benim içine geçmek FragmentPagerAdapterve aramak:

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

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

bu da setVariableHeight()benim CustomViewPageruygulamamı aramamı sağlıyor :

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

En iyi yaklaşım olduğundan emin değilim, iyi / kötü / kötü olduğunu düşünüyorsanız yorumları seveceksiniz, ancak benim uygulamada oldukça iyi çalışıyor gibi görünüyor :)

Umarım bu birisine yardım eder!

DÜZENLEME: Bir requestLayout()aramadan sonra eklemeyi unuttum super.measure()(aksi takdirde görünümü yeniden çizmez).

Ayrıca ebeveynin dolgusunu son yüksekliğe eklemeyi unuttum.

Ayrıca orijinal genişlik / yükseklik MeasureSpecs gerektiği gibi yeni bir tane oluşturmak lehine tutarak düştü. Kodu buna göre güncelleyin.

Sahip olduğum başka bir sorun, kendini doğru bir şekilde boyutlandırmaması ScrollViewve suçlu MeasureSpec.EXACTLYyerine çocuk ölçmek olduğunu bulundu MeasureSpec.UNSPECIFIED. Bunu yansıtacak şekilde güncellendi.

Bu değişikliklerin tümü koda eklenmiştir. İsterseniz eski (yanlış) sürümleri görmek için geçmişi kontrol edebilirsiniz.


Neden unuttuğunuzu koda eklemiyorsunuz?
hasan

@hasan zaten yaptım, herhangi bir karışıklık için üzgünüm! Cevabı da söyleyecek şekilde güncelleyecek
logan

Müthiş! Memnun oldum :)
logan

8

Başka bir çözüm, ViewPageryüksekliği, sayfadaki geçerli sayfa yüksekliğine göre güncellemektir PagerAdapter. ViewPagerSayfalarınızı şu şekilde oluşturduğunuzu varsayarsak :

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

Nerede mPagesiç listesi PageInfodinamik ilave yapılar PagerAdapterve CustomImageViewsadece normal bir ImageViewoverriden ile onMeasure()belirtilen genişliğe göre yüksekliğini ayarlar ve görüntü en boy oranı tutan yöntem.

ViewPagerYüksekliği setPrimaryItem()yöntemde zorlayabilirsiniz :

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

Not edin Math.max(height, 1). Bu ViewPager, görüntülenen sayfayı güncellemeyen sinir bozucu hatayı düzeltir (boş gösterir), önceki sayfa sıfır yüksekliğe (yani içinde boş çizilebilir CustomImageView) sahip olduğunda, her bir garip iki sayfa arasında ileri geri kaydırılır .


bana doğru yolu takip gibi görünüyor ama yöntemlerde item.mImageView.measure(..)doğru boyutları almak için reklam a gerekiyordu getMeasuredXXX().
Gianluca P.

6

Viewpager içinde statik içerik kullanırken ve süslü bir animasyon istemiyorsanız, aşağıdaki görünüm çağrı cihazını kullanabilirsiniz

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

Bu iyi çalışıyor. Çocukların arasından geçip maksimum boyda olanı alarak uzattım.
Javier Mendonça

Geri dönüşümcü görünümü altında bile iyi çalışıyor
kanudo

Bu istisna-java.lang.NullPointerException alıyorum: Bir null nesne başvurusu
voj

Ama ilk elementi almak yanlış olabilir.
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);
}

4

Patlamış mısır zaman android app kaynak kodundan ben mevcut çocuğun boyutuna bağlı olarak güzel animasyon ile viewpager boyutunu dinamik olarak ayarlayan bu çözümü buldum.

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

4

Sadece en büyüğüne değil, her çocuğa boyutunu ayarlayan ViewPager'e ihtiyacınız varsa , bunu yapan bir kod parçası yazdım. Bu değişiklik üzerine animasyon olmadığını unutmayın (benim durumumda gerekli değil)

android: minHeight bayrağı da desteklenmektedir.

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

Bu nedenle, adaptördeki öğelerin miktarı değiştiğinde bu adaptörün büyük bir sorunu var
jobbert

İfadenizi netleştirebilir misiniz?
Phatee P

Her çocuk başlangıçta hesaplanmadığından bu kod nullpoint'lere neden olabilir. Bir sekme düzeni deneyin ve 1'den 5'e veya kod olarak ilerleyin ve göreceksiniz.
jobbert

4

Geliştirilmiş Daniel López Lacalle içinde yeniden yazılmış cevap, 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)
    }
}

3

Aynı sorunla karşılaştım ve ayrıca kullanıcı sayfalar arasında ilerlediğinde ViewPager'i içeriklerinin etrafına sarmak zorunda kaldım. Siberjenin yukarıdaki cevabını kullanarak onMeasure yöntemini şu şekilde tanımladım:

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

Bu şekilde, onMeasure yöntemi, ViewPager tarafından görüntülenen geçerli sayfanın yüksekliğini ayarlar.


Cevabınızla yalnızca en yüksek içerik görünür, diğer içerik kaybolur ...
Blaze Tama

2

Yukarıda önerilen hiçbir şey benim için işe yaramadı. Kullanım durumumda 4 özel ViewPagers var ScrollView. Üstleri en boy oranına göre ölçülür ve geri kalanı sadece sahiptir layout_height=wrap_content. Cybergen , Daniel López Lacalle çözümlerini denedim . Hiçbiri benim için tam olarak çalışmıyor.

Sanırım siberjenin sayfa> 1'de neden çalışmadığı, sayfa 1'e göre çağrı cihazının yüksekliğini hesaplamasıdır, bu da daha fazla kaydırırsanız gizlenir.

Hem siber hem de Daniel López Lacalle önerileri benim durumumda garip bir davranış sergiliyor : 3'ten 2'si tamam yüklendi ve 1 rastgele yükseklik 0. onMeasureÇocuk nüfusundan önce çağrıldı. Bu yüzden bu 2 cevabın + kendi düzeltmelerimin bir karışımını buldum:

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

Fikir, ViewPagerçocukların boyutlarını hesaplamak ve ilk sayfanın hesaplanan yüksekliğini düzen parametrelerine kaydetmektir ViewPager. Parçanın düzen yüksekliğini ayarlamayı unutmayın, wrap_contentaksi takdirde height = 0 alabilirsiniz. Bunu kullandım:

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

Tüm sayfalarınız aynı yüksekliğe sahipse bu çözümün harika çalıştığını lütfen unutmayın . Aksi takdirde ViewPager, aktif çocuğa bağlı olarak yüksekliği yeniden hesaplamanız gerekir . İhtiyacım yok, ancak çözümü önerirseniz cevabı güncellemekten memnuniyet duyarız.


Bunca yıldan sonra hala cevabınızı güncelleyebilir misiniz? Bana bir ton yardımcı olur
Denny

2

Bu sorunu yaşayan ve X # için Xamarin Android kodlama insanlar için, bu da hızlı bir çözüm olabilir:

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

Bu, temel olarak çocuğunuzun görüşleri aynı yükseklikte olduğunda kullanışlıdır. Aksi takdirde, kontrol ettiğiniz tüm çocuklara bir tür "minimumYükseklik" değeri depolamanız istenir ve o zaman bile daha küçük çocuk görünümlerinizin altında görünür boş alanlar olmasını istemeyebilirsiniz.

Çözümün kendisi benim için yeterli değil, ancak bunun nedeni alt öğelerimin listViews olması ve MeasuredHeight'ın doğru bir şekilde hesaplanmaması gibi görünüyor.


Bu benim için çalıştı. Vizördeki tüm çocuğum görüşleri aynı yükseklikte.
Dmitry

2

Seçili geçerli alt görünümünde üst görünümün yükseklik tabanını yeniden boyutlandıracak API 23 önce doğru çalışan WrapContentHeightViewPager bir sürümü var.

API 23'e yükselttikten sonra çalışmayı durdurdu. Eski çözümün, getChildAt(getCurrentItem())hangisinin çalışmadığını ölçmek için mevcut çocuk görüşünü elde etmek için kullandığı ortaya çıktı . Buradaki çözüme bakın: https://stackoverflow.com/a/16512217/1265583

Aşağıda API 23 ile çalışır:

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

Teşekkür ederim!! Cevapları saatlerdir deniyorum ve bu benim için tamamen işe yarayan tek şey. 'SetPrimaryItem () `öğesinin çağrı cihazında, bir sekmeden diğerine geçerken requestLayout()yükseklik ayarlandığından çağrı yapan bir işlev çağırdığı özel bir bağdaştırıcı ile birleştirilmesi gerekir . Neden superiki kez aranması gerektiğini hatırlıyor musunuz ? Aksi halde çalışmayacağını fark ettim.
M3RS

API 28 ile çalışır.
Khalid Lakhani

2

Aşağıdaki kod benim için çalışan tek şey

1. Bir HeightWrappingViewPager bildirmek için bu sınıfı kullanın:

 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. Yükseklik kaydırma görünümü çağrı cihazını xml dosyanıza ekleyin:

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

3. Görüntüleme cihazınızı bildirin:

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

Teşekkürler. Bu işe yaradı. Peki neden android ekibi bunu kod tabanlarında bulamıyor?
Mohanakrrishna

Kendinizi özelleştirmek zorunda olduğunuz şeylerden biri de ihtiyacınıza bağlıdır, ayrıca bu yıl 2019 Google I / O'da google viewPager2'yi tanıttı ve 2011'de oluşturulan eski ViewPager'in yerine geçiyor, 'androidx.viewpager2: viewpager2 : 1.0.0-alpha04 '
Hossam Hassan

2

Seçilen öğeye bağlı olarak yüksekliği değiştirmek için viewpager yapmak için siberjen cevabını düzenliyorum Sınıf siberjeninkiyle aynı, ancak tüm görüntüleyicinin alt görüntüleme yükseklikleri olan bir tamsayı Vektörü ekledim ve yüksekliği değiştirmek için sayfa değiştiğinde erişebiliriz

Bu sınıf:

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

Ardından etkinliğinize bir OnPageChangeListener ekleyin

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

Ve işte xml:

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

Lütfen gerekirse İngilizcemi düzeltin


Bunun bazı sorunları var. heightsListe sonsuzluğu artabilir.
rosuh

@rosuh Sorunla ne zaman karşılaştınız? Bu sadece ViewPager ile TabLayout kullanılan, bu yüzden her yerde iyi çalışır emin değilim
geggiamarti

@geggiamarti Sorun, bazı sayfaların geri dönüştürülmesidir. Ve kullanıcı onlara tokatlamak yeniden oluşturulan bu nedenle bu measurebirden çok kez çağrı olacaktır. Yükseklik listesini artırabilir. Başka bir durum, kullanıcının bu viewPager için arayabileceği requestLayout(veya setLayoutParamsyöntem, yaptığınız gibi), aynı zamanda çoklu süreleri de ölçmesidir.
rosuh

1

Eğer ViewPagerkullandığınız bir alt öğesi olan ScrollView VE bir sahiptir PagerTitleStripçocuğu zaten sağlanan büyük yanıtlar hafif bir değişiklik kullanmanız gerekir. Referans için benim XML şöyle görünür:

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

Gözlerinde farklı onMeasureMecbur ADD ait measuredHeight PagerTitleStripbiri bulunursa. Aksi takdirde, ek yer kaplamasına rağmen yüksekliği tüm çocukların en büyük yüksekliği olarak kabul edilmeyecektir.

Umarım bu başka birine yardımcı olur. Üzgünüm bu bir hack biraz ...

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

1

Burada gördüğüm çözümlerin çoğu çift ölçüm yapıyor gibi görünüyor: önce çocuk görüşlerini ölçmek ve sonra super.onMeasure()

WrapContentViewPagerDaha verimli, RecyclerView ve Fragment ile iyi çalışan bir gelenek buldum

Demoyu buradan kontrol edebilirsiniz:

github / ssynhtn / WrapContentViewPager

ve sınıfın kodu burada: WrapContentViewPager.java


0

Benzer (ama daha karmaşık bir senaryo) var. Bir ViewPager içeren bir iletişim kutusu var.
Alt sayfalardan biri kısa, statik bir yüksekliği var.
Başka bir alt sayfa her zaman mümkün olduğunca uzun olmalıdır.
Başka bir alt sayfa bir ScrollView içerir ve ScrollView içeriği iletişim kutusunda kullanılabilecek tam yüksekliğe ihtiyaç duymuyorsa sayfa (ve dolayısıyla iletişim kutusunun tamamı) WRAP_CONTENT olmalıdır.

Mevcut cevapların hiçbiri bu özel senaryo için tamamen işe yaramadı. Bekle - bu inişli çıkışlı bir yolculuk.

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

Görünümleri ölçmek ve dekor yüksekliğini ölçmek için kod için @Raanan'a çok teşekkürler. Kitaplığıyla ilgili problemlerle karşılaştım - animasyon kekeledi ve bence ScrollView, iletişim kutusunun yüksekliği gerektirecek kadar kısa olduğunda kaydırılmayacaktı.


0

benim durumumda ekleme clipToPaddingsorunu çözdü.

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

Şerefe!


0

Ben android ekleyerek benim durumum: fillViewport = "true" sorunu çözdü


0

Benim durumumda, boyutu seçerken seçili öğe ve animasyon için bir wrap_content ile bir viewpager gerekiyordu. Aşağıda uygulamamı görebilirsiniz. Birisi işe yarayabilir mi?

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

Projeye attrs.xml ekleyin:

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

Ve kullan:

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

0

Bu ViewPager yalnızca geçerli görünür çocuklara yeniden boyutlandırılır (gerçek çocuklarının en büyüğü değil)

Https://stackoverflow.com/a/56325869/4718406 adresinden fikir

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

}


0

ViewPager'in yüksekliğini ölçün:

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

}

call setPrimaryView (Görüntüle) :

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

}

0

ViewPager'in üst düzenini şu şekilde verin: 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>

Ayarlamayı unutma android:fillViewport="true"

Bu görünüm görünümünü doldurmak için kaydırma görünümünü ve çocuğunun içeriğini genişletir.

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


0

ViewPager2'ye geçebilirsiniz. ViewPager'in güncellenmiş bir sürümüdür. ViewPager ile aynı şeyi yapar, ancak daha akıllı ve verimli bir şekilde. ViewPager2 çeşitli yeni özelliklerle birlikte gelir. Elbette İçeriği Sarma sorunu ViewPager2 tarafından çözülmüştür.

Android belgelerinden: "ViewPager2, sağdan sola mizanpaj desteği, dikey yönlendirme, değiştirilebilir Parça koleksiyonları vb.

Bu makaleyi yeni başlayanlar için tavsiye ederim:

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


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.