Android: я не можу мати ViewPager WRAP_CONTENT


258

У мене встановлений простий ViewPager, який має ImageView висотою 200dp на кожній сторінці.

Ось мій пейджер:

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, пейджер завжди заповнює екран, навіть якщо перегляд зображення лише 200dp. Я намагався замінити висоту пейджера на "200", але це дає мені різні результати з кількома роздільними можливостями. Я не можу додати "dp" до цього значення. Як додати 200dp до макета пейджера?


1
будь-ласка, позначте зірочку code.google.com/p/android/isissue/detail?id=54604
Христос

Відповіді:


408

Перевиконання вашої міри, викладене ViewPagerнижче, змусить її набути зріст найбільшої дитини, яку вона наразі має.

@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
Це найближче до того, що мені потрібно, але додати два речі: 1. ViewPager змінює розмір лише найбільшого з фактичних дітей, тобто лише видимого на даний момент предмета та безпосередньо суміжних. Виклик setOffscreenPageLimit (загальна кількість дітей) на ViewPager вирішує це і призводить до перегляду ViewPager, розмір якого встановлений найбільшим з усіх його елементів і ніколи не змінює розмір. 2. У веб-переглядах виникають деякі дивні проблеми при спробі їх виміряти. Виклик requestLayout () у WebView після завантаження чогось вирішує це.
0101100101

3
Є лише невелика проблема, яку я вирішу: якщо viewPager має видимість GONE, і ви встановите його на видиме, onMeasure викликається перед тим, як його фрагмент буде створений. Так що в кінцевому підсумку буде висота 0. Якщо хтось має ідею, він вітається. Я думаю, я піду зворотним
дзвінкам,

4
Це не спрацює, якщо у вас є дитячі подання декору - це тому, що ViewPager.onMeasure () вимірює погляди декору та виділяє їм спочатку простір, а потім надає решту місця дітям, які не декоруються. Тим не менш, це на сьогодні найменш неправильне рішення, тому я підтримав заяву;)
Бенджамін Добел

3
Я продовжую повертатися до цього кожен раз , коли я використовую ViewPager
Ono

7
getChildCount () може повернути 0, поки ви вже виконували setAdapter () на ViewPager! Фактичний виклик populate () (який створює представлення даних) відбувається всередині super.onMeasure (widthMeasureSpec, heightMeasureSpec); дзвінок. Постановка додаткового дзвінка super.onMeasure () на початку цієї функції зробила трюк. Також перевірте stackoverflow.com/questions/38492210 / ...
southerton

106

Ще одне загальне рішення - приступити wrap_contentдо просто роботи.

Я продовжив ViewPagerперемогу onMeasure(). Висота обертається навколо першого дитячого виду. Це може призвести до несподіваних результатів, якщо погляди дитини будуть не однакової висоти. Для цього клас можна легко розширити, скажімо, анімувати до розміру поточного перегляду / сторінки. Але мені це не було потрібно.

Ви можете використовувати цей ViewPager у своїх макетах XML так само, як оригінальний ViewPager:

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

Перевага: Цей підхід дозволяє використовувати ViewPager в будь-якому макеті, включаючи RelativeLayout, для накладання інших елементів інтерфейсу.

Залишається один недолік: якщо ви хочете використовувати поля, вам потрібно створити два вкладені макети та надати внутрішньому бажане поле.

Ось код:

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
хтось ще отримав порожню сторінку поруч із поточним елементом, коли оглядач знищився та відкрився знову?
Zyoo

1
У мене теж пусті сторінки.
aeren

10
Вам просто потрібно об'єднати дві верхні відповіді цього питання , як це описано в моєму блозі: pristalovpavel.wordpress.com/2014/12/26 / ...
Аніл

4
Просто замініть код методу "onMeasure" на відповідь, яку дав "Даніель Лопес Лакаль".
Йогу Гуру

1
Чудово ..! Працював для мене .. @cybergen Спасибі багато, що врятував мій день ..!
Днянеш М

59

Я базував свою відповідь на Даніелі Лопес Лакалле і на цьому дописі http://www.henning.ms/2013/09/09/viewpager-that-simply-dont-measure-up/ . Проблема з відповіддю Даніеля полягає в тому, що в деяких випадках мої діти мали висоту нуля. Рішення було, на жаль, виміряти двічі.

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

Це також дозволяє встановити висоту на ViewPager, якщо ви цього хочете або просто перегорнете_міст.


У мене була така ж проблема, і я вирішив її своєю відповіддю, дякую. Але будь-яке пояснення, чому?
Барт-бург

Я думаю, що вони не збиралися підтримувати контент для обгортання, тому що я не думаю, що вони вважали це нормальним випадком використання. Щоб підтримати це, ми повинні повторно виміряти себе після вимірювання наших дітей, щоб ми могли обернути вміст.
MinceMan

Чому зображення в цьому ViewPager дійсно коротші, ніж зображення у ImageView, який використовує те саме scaleTypeі аналогічно, layout_width=match_parentяк і layout_height=wrap_content? там відсутнє 20dp.
Акула

Акула, я справді не впевнений. Це може мати щось спільне з тим, що насправді робить ваш тип масштабу. Ви можете спробувати встановити висоту.
MinceMan

1
Я не можу вірити в це! Я провів 2 дні, склеюючи свій власний переглядач, і застряг у проблемі, коли мій початковий погляд не з’явиться, і я просто не міг зрозуміти, чому! // super has to be called in the beginning so the child views can be initialized.<----- ЦЕ було причиною, довелося викликати це на початку та в кінці функції onMeasure. Yippiii, віртуальна висока п'ятірка сьогодні на мені!
Starwave

37

Я щойно відповідав на дуже подібне запитання з цього приводу і траплявся, коли знайшов посилання, щоб підкріпити свої претензії, тож пощастило вам :)

Моя інша відповідь:
ViewPager не підтримує, wrap_contentоскільки він (зазвичай) ніколи не завантажує всіх своїх дітей одночасно, і тому не може отримати відповідний розмір (можливим буде мати пейджер, який змінює розмір кожного разу при переключенні сторінки).

Однак ви можете встановити точний розмір (наприклад, 150dp) і також match_parentпрацює.
Ви також можете динамічно змінювати параметри свого коду, змінюючи height-attribute в його LayoutParams.

Для своїх потреб ви можете створити ViewPager у власному xml-файлі, з layout_height встановити 200dp, а потім у своєму коді, а не створювати новий ViewPager з нуля, ви можете надути цей xml-файл:

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

3
Гарна відповідь, щось дратує те, що поведінка за замовчуванням - "зробити щось дещо незрозуміле". Дякую за пояснення.
Кріс Вандевельде

8
@ChrisVandevelde, здається, це звичайний орендар деяких бібліотек Android. Як тільки ви дізнаєтесь основи, ви зрозумієте, що за ними нічого не слідує
CQM

1
Але @Jave, чому нахил переглядача не може регулювати свою висоту щоразу, коли його діти завантажують?
Diffy

@CQM справді! Бібліотека ViewPagerIndicator має таку ж проблему, що і з layout_heightналаштуванням wrap_content, але це ще гірше, оскільки простий спосіб встановити її на фіксовану кількість не працює.
Джуліо Піанкастеллі

20

Використовуючи відповідь Даніеля Лопеса Локаля , я створив цей клас у Котліні. Сподіваюся, це заощадить більше часу

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

Я вже стикався з цим питанням у кількох проектах і ніколи не мав повного рішення. Тому я створив проект github WrapContentViewPager як замість ViewPager на місці.

https://github.com/rnevet/WCViewPager

Рішення було натхнене деякими відповідями тут, але вдосконалюється щодо:

  • Динамічно змінює висоту ViewPager відповідно до поточного перегляду, включаючи під час прокрутки.
  • Враховує висоту виду "декору", як PagerTabStrip.
  • Враховує всі накладки.

Оновлено для бібліотеки підтримки версію 24, яка порушила попередню реалізацію.


@mvai Ви можете відкрити проблему чи роздрібнити її та змінити зразок програми?
Raanan

1
Я з’ясував, що RecyclerView також має деякі проблеми з wrap_content; він працює, якщо ви використовуєте користувацький LinearLayoutManager, як це . Тож нічого поганого у вашій бібліотеці немає.
natario

1
Що ще має бути виправлено - це використання з FragmentStatePagerAdapter. Схоже, це вимірювання дітей до того, як фрагменти будуть викладені, тим самим надаючи менший зріст. Те, що для мене спрацювало, - відповідь @logan, хоча я все ще працюю над цим. Ви можете спробувати об'єднати такий підхід у свою бібліотеку. Я не знайомий з github, вибачте.
natario

Дякую, я погляну на це.
Raanan

1
Для тих, хто цікавиться, як зробити цю роботу за допомогою FragmentPagerAdapter, змусьте ваш адаптер реалізувати ObjectAtPositionInterface, зберігаючи список фрагментів внутрішньо, щоб він міг повернути відповідний фрагмент із методу getObjectAtPosition.
Пабло

15

Я просто наткнувся на те саме питання. У мене був ViewPager, і я хотів відобразити оголошення на кнопці. Я знайшов рішення, щоб передати пейджер у RelativeView і встановити його layout_a над ідентифікатором подання, який я хочу бачити під ним. що працювало на мене.

ось мій макет 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
тільки для довідки, вам не потрібно xmlns: android = " schemas.android.com/apk/res/android " в обох, лише в першому.
Мартін Маркончіні

2
Ваше питання зовсім не було однаковим. Ваш макет прекрасно працює, якщо ViewPager встановлений на match_parent - в ОП була ситуація, коли він хотів, щоб ViewPager завернув його вміст.
k2col

9

Я також зіткнувся з цією проблемою, але в моєму випадку у мене виникло таке, FragmentPagerAdapterщо постачало ViewPagerйого сторінки. Проблема, яку я мав, полягала в тому, onMeasure()що ViewPagerвиклик був викликаний до того, як Fragmentsбуло створено будь-яке (і тому він не міг правильно розмістити себе).

Після невеликих проб і помилок я виявив, що finishUpdate()метод FragmentPagerAdapter викликається після Fragmentsініціалізації (з instantiateItem()в FragmentPagerAdapter), а також після / під час прокрутки сторінки. Я зробив невеликий інтерфейс:

public interface AdapterFinishUpdateCallbacks
{
    void onFinishUpdate();
}

яку я передаю в свою FragmentPagerAdapterі закликаю:

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

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

що, в свою чергу, дозволяє задіяти setVariableHeight()мою 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;
    }
}

Я не впевнений, що це найкращий підхід, я хотів би коментувати, якщо ви вважаєте, що це добре / погано / зло, але, здається, він працює досить добре в моїй реалізації :)

Сподіваюся, це допомагає комусь там!

EDIT: Я забув додати requestLayout()після виклику super.measure()(інакше це не переробить представлення).

Я також забув додати батьківську підкладку до кінцевої висоти.

Я також відмовився зберігати оригінальну MeasureSpecs ширини / висоти на користь створення нової відповідної потреби. Оновили код відповідно.

Іншою проблемою у мене було те, що він не розмістив би себе правильно в ScrollViewі виявив, що винуватець заміряє дитину MeasureSpec.EXACTLYзамість MeasureSpec.UNSPECIFIED. Оновлено, щоб відобразити це.

Ці зміни були додані до коду. Ви можете перевірити історію, щоб побачити старі (неправильні) версії, якщо хочете.


Чому ви не додасте в код тих, кого ви забули.
Хасан

@hasan Я вже робив, вибачте за будь-яку плутанину! Буде оновлено відповідь, щоб сказати, що також
logan

Дивовижно! Радий, що це допомогло :)
logan

8

Ще одне рішення - оновити ViewPagerвисоту відповідно до поточної висоти сторінки в ній PagerAdapter. Якщо припустити, що ви створюєте свої ViewPagerсторінки таким чином:

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

Де mPagesвнутрішній список PageInfoструктур, що динамічно додається до PagerAdapterта CustomImageViewє просто регулярним методом ImageViewoverriden, onMeasure()який встановлює його висоту відповідно до заданої ширини і зберігає співвідношення сторін зображення.

Ви можете примусити ViewPagerвисоту setPrimaryItem()методом:

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

Зверніть увагу на Math.max(height, 1). Це виправляє дратівливу помилку, ViewPagerяка не оновлює відображувану сторінку (показує її порожньою), коли попередня сторінка має нульову висоту (тобто нульове малювання в CustomImageView), кожен непарний проведіть вперед-назад між двома сторінками.


мені здається правильний шлях, але мені потрібно було оголосити, item.mImageView.measure(..)щоб отримати правильні розміри getMeasuredXXX()методів.
Джанлука П.

6

Якщо ви використовуєте статичний вміст у вікні перегляду, і ви не бажаєте фантазії, ви можете використовувати наступний пейджер перегляду

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

Це чудово працює. Я продовжив це, провівши через дітей і взявши той, з максимальною висотою.
Хав'єр Мендонча


Я отримую це виняток - java.lang.NullPointerException: Спроба викликати віртуальний метод 'void android.view.View.measure (int, int)' у нульовій посилання на об'єкт
PJ2104

Але взяти перший елемент може бути неправильним.
Тобіас Райх

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

З вихідного коду програми Popcorn time для Android я знайшов це рішення, яке динамічно коригує розмір проглядача з приємною анімацією залежно від розміру поточної дитини.

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

Якщо вам потрібен ViewPager, який регулює його розмір для кожної дитини , а не лише до найбільшої, я написав фрагмент коду, який це робить. Зауважте, що при цій зміні немає анімації (не потрібно в моєму випадку)

android: minHeight прапор також підтримується.

public class ChildWrappingAdjustableViewPager extends ViewPager {
    List<Integer> childHeights = new ArrayList<>(getChildCount());
    int minHeight = 0;
    int currentPos = 0;

    public ChildWrappingAdjustableViewPager(@NonNull Context context) {
        super(context);
        setOnPageChangeListener();
    }

    public ChildWrappingAdjustableViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        obtainMinHeightAttribute(context, attrs);
        setOnPageChangeListener();
    }

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

        //calculate child views
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            if (h < minHeight) {
                h = minHeight;
            }
            childHeights.add(i, h);
        }

        if (childHeights.size() - 1 >= currentPos) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeights.get(currentPos), MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private void obtainMinHeightAttribute(@NonNull Context context, @Nullable AttributeSet attrs) {
        int[] heightAttr = new int[]{android.R.attr.minHeight};
        TypedArray typedArray = context.obtainStyledAttributes(attrs, heightAttr);
        minHeight = typedArray.getDimensionPixelOffset(0, -666);
        typedArray.recycle();
    }

    private void setOnPageChangeListener() {
        this.addOnPageChangeListener(new SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                currentPos = position;

                ViewGroup.LayoutParams layoutParams = ChildWrappingAdjustableViewPager.this.getLayoutParams();
                layoutParams.height = childHeights.get(position);
                ChildWrappingAdjustableViewPager.this.setLayoutParams(layoutParams);
                ChildWrappingAdjustableViewPager.this.invalidate();
            }
        });
    }
}

Таким чином, у цього адаптера виникає величезна проблема, коли кількість елементів у адаптері змінюється
робота

Ви можете уточнити свою заяву?
Phatee P

Цей код може викликати нульові покажчики, оскільки не кожна дитина розраховується на початку. Спробуйте розкладку вкладок і прокрутіть від 1 до 5 або мудрий код, і ви побачите це.
Вакансія

4

Удосконалена відповідь Даніеля Лопеса Лакале , переписана в Котліні :

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

Я наткнувся на той самий випуск, і мені також довелося змусити ViewPager обгортати його вміст, коли користувач прокручував сторінки. Використовуючи вищезгадану відповідь на кіберген, я визначив метод onMeasure наступним чином:

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

Таким чином, метод onMeasure встановлює висоту поточної сторінки, відображеної ViewPager.


З вашою відповіддю з’являється лише найвищий вміст висоти, інший вміст зникає ...
Blaze Tama

2

Нічого із запропонованих вище мені не працювало. У моєму випадку використання 4 спеціальних ViewPagers в ScrollView. Верхня частина їх вимірюється на основі співвідношення сторін, а решта - просто layout_height=wrap_content. Я спробував кіберген , рішення Даніеля Лопеса Лакале . Жоден з них не працює на мене повною мірою.

Я здогадуюсь, чому кіберген не працює на сторінці> 1, тому що він обчислює висоту пейджера на основі сторінки 1, що приховано, якщо прокрутити далі.

І пропозиції кібергена, і Даніеля Лопеса Лакалле мають дивну поведінку в моєму випадку: 2 з 3 завантажуються нормально, а 1 випадково висота - 0. З'являється, що onMeasureвикликали до того, як діти заселялися. Тому я придумав суміш цих 2 відповідей + мої власні виправлення:

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

Ідея полягає в тому, щоб дозволити ViewPagerобчислити розміри дітей і зберегти обчислену висоту першої сторінки в параметах макета ViewPager. Не забудьте встановити висоту компонування фрагмента, wrap_contentінакше ви можете отримати висоту = 0. Я користувався цим:

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

Зверніть увагу, що це рішення чудово працює, якщо всі ваші сторінки мають однакову висоту . Інакше потрібно перерахунокViewPager висоту з урахуванням поточної активності дитини. Мені це не потрібно, але якщо ви запропонуєте рішення, я би радив оновити відповідь.


Чи можете ви все-таки оновити свою відповідь після всіх цих років? Допоможе мені тонну
Денні

2

Для людей, які мають цю проблему та кодують Xamarin Android на C #, це також може бути швидким рішенням:

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

Це в основному корисно, якщо погляди вашої дитини однакової висоти. В іншому випадку вам потрібно буде зберігати якесь значення "мінімальна висота" над усіма дітьми, проти яких ви перевіряєте, і навіть тоді ви, можливо, не захочете мати порожні місця під вашими меншими уявленнями про дітей.

Але саме для мене рішення недостатньо, але це тому, що мої дочірні предмети - це списокViews, і їх MeasuredHeight не обчислюється правильно, здається.


Це працювало для мене. Усі мої дитячі погляди у вікні перегляду однакової висоти.
Дмитро

2

У мене є версія WrapContentHeightViewPager, яка працювала коректно перед API 23, яка дозволить змінити базу висоти батьківського виду для поточного вибраного дочірнього виду.

Після оновлення до API 23 він перестав працювати. Виявляється, старе рішення використовувалося getChildAt(getCurrentItem())для отримання поточного уявлення про дитину для вимірювання, яке не працює. Дивіться рішення тут: https://stackoverflow.com/a/16512217/1265583

Нижче працює з 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);
}

Дякую!! Я годинами намагаюся відповісти, і це єдине, що повністю працює для мене. Його потрібно поєднувати з користувацьким адаптером, де 'setPrimaryItem ()' викликає функцію в пейджері, яка викликає requestLayout()таку висоту, що регулюється, переходячи з однієї вкладки на іншу. Ви пам’ятаєте, чому superпотрібно дзвонити двічі? Я помітив, що інакше не вийде.
M3RS

Працює з API 28.
Халід Лахані

2

Код нижче - це єдине, що працювало для мене

1. Використовуйте цей клас для оголошення HeightWrappingViewPager:

 public class HeightWrappingViewPager extends ViewPager {

        public HeightWrappingViewPager(Context context) {
            super(context);
        }

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

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int mode = MeasureSpec.getMode(heightMeasureSpec);
            // Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT.
            // At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT.
            if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
                // super has to be called in the beginning so the child views can be initialized.
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                int height = 0;
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                    int h = child.getMeasuredHeight();
                    if (h > height) height = h;
                }
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            }
            // super has to be called again so the new specs are treated as exact measurements
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

2. Вставте пейджер вищого обгортання у ваш XML-файл:

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

3. Оголосіть свій пейджер перегляду:

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

Дякую. Це спрацювало. Але чому не може андроїд команда мати це у своїй кодовій базі?
Mohanakrrishna

Це одне з речей, які ви повинні налаштувати самостійно, залежить від ваших потреб, також Google представив viewPager2 в цьому році 2019 вводу-виводу Google і є заміною старого ViewPager, створеного в 2011 році, реалізація 'androidx.viewpager2: viewpager2 : 1.0.0-alpha04 '
Хоссам Хассан

2

Я редагую відповідь на кіберген, щоб зробити viewpager, щоб змінити висоту залежно від вибраного елемента. Клас такий самий, як кібергенів, але я додав вектор цілих чисел, який є всіма висотами перегляду дітей, що переглядають, і ми можемо отримати доступ до нього при зміні сторінки для оновлення висоти

Це клас:

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

Потім у свою діяльність додайте OnPageChangeListener

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

І ось xml:

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

Будь ласка, виправте мою англійську, якщо потрібно


У цьому є деякі проблеми. heightsСписок може збільшитися в нескінченність.
rosuh

@rosuh Коли у вас виникли проблеми? Я використовував це лише в TabLayout з ViewPager, тому я не впевнений, чи добре він працює скрізь
geggiamarti

@geggiamarti Проблема в тому, що деякі сторінки будуть перероблені. І відтворено, коли користувач проведіть до них, тому це measureбуде дзвінок у кілька разів. Це може збільшити список висот. Інша ситуація, коли користувач може викликати requestLayout(або setLayoutParamsметод, як і те, що ви робили) для цього viewPager вручну, також вимірюватиме кілька разів.
rosuh

1

Якщо ViewPagerви користуєтеся дитиною ІНІ ScrollView і має PagerTitleStripдитину, вам доведеться скористатися незначною модифікацією вже наданих чудових відповідей. Для довідки мій XML виглядає так:

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

У вашому випадку onMeasureви повинні додати вимірювану висотуPagerTitleStrip якщо такий знайдено. Інакше його зріст не вважатиметься найбільшою висотою всіх дітей, навіть якщо він займає додаткове місце.

Сподіваюсь, це допомагає комусь іншому. Вибачте, що це трохи хак ...

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

Більшість рішень, які я бачу тут, роблять подвійне вимірювання: спочатку вимірюють дитячі погляди, а потім викликають super.onMeasure()

Я придумав WrapContentViewPagerбільш ефективний звичай , добре працює з RecyclerView та Fragment

Ви можете перевірити демонстрацію тут:

github / ssynhtn / WrapContentViewPager

і код класу тут: WrapContentViewPager.java


0

У мене схожий (але складніший сценарій). У мене діалогове вікно, яке містить ViewPager.
Одна з дочірніх сторінок коротка, зі статичним зростом.
Інша дочірня сторінка завжди повинна бути максимально високою.
Інша дочірня сторінка містить ScrollView, і сторінка (і, отже, весь діалог) повинна WRAP_CONTENT, якщо вмісту ScrollView не потрібна повна висота, доступна для цього діалогу.

Жоден із існуючих відповідей не працював повністю для цього конкретного сценарію. Тримайся - це вибаглива їзда.

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

Велике спасибі @Raanan за код для вимірювання подань та вимірювання висоти декору. У мене виникли проблеми з його бібліотекою - анімація заїкалася, і я думаю, що мій ScrollView не прокручується, коли висота діалогу була достатньо короткою, щоб цього вимагати.


0

у моєму випадку додавання clipToPaddingвирішило проблему.

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

Ура!



0

У моєму випадку мені потрібен був проглядач з конвертом_контенту для обраного на даний момент елемента та анімації при застосуванні розміру. Нижче ви можете побачити мою реалізацію. Може хтось стане у нагоді.

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

Додати attrs.xml у проект:

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

І використовуйте:

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

0

Цей ViewPager змінює розмір лише для теперішніх видимих ​​дітей (не найбільших з реальних дітей)

Ідея від https://stackoverflow.com/a/56325869/4718406

public class DynamicHeightViewPager extends ViewPager {

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

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



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

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

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


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

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

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

}


0

Виміряйте висоту ViewPager:

public class WrapViewPager extends ViewPager {
    View primaryView;

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (primaryView != null) {
            int height = 0;
            for (int i = 0; i < getChildCount(); i++) {
                if (primaryView == getChildAt(i)) {
                    int childHeightSpec = MeasureSpec.makeMeasureSpec(0x1 << 30 - 1, MeasureSpec.AT_MOST);
                    getChildAt(i).measure(widthMeasureSpec, childHeightSpec);
                    height = getChildAt(i).getMeasuredHeight();
                }

            }

            setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        }
    }

    public void setPrimaryView(View view) {
        primaryView = view;
    }

}

set callPrimaryView (Перегляд):

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

Не забудьте встановити android:fillViewport="true"

Це розтягне перегляд прокрутки та вміст його дитини, щоб заповнити вікно перегляду.

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


0

Ви можете перейти на ViewPager2. Це оновлена ​​версія ViewPager. Це робить те саме, що і ViewPager, але розумнішим та ефективнішим способом. ViewPager2 постачається з різноманітними новими можливостями. Звичайно, проблему з обгортанням вмісту вирішила ViewPager2.

З Документів Android: "ViewPager2 замінює ViewPager, звертаючись до більшості больових точок попередника, включаючи підтримку планування справа наліво, вертикальну орієнтацію, модифіковані колекції фрагментів тощо".

Я рекомендую цю статтю для початківців:

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


Це видання все ще є. Перевірте issueetracker.google.com/u/0/isissue/143095219
Сомеш Кумар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.