Android: у меня не работает ViewPager WRAP_CONTENT
Я установил простой 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 в макет пейджера?
22 ответа
Переопределение по вашему 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);
}
Еще одно более общее решение заключается в том, чтобы получить 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;
}
}
Я основал свой ответ на Даниэле Лопесе Лакалье и на этом посте 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, если вы этого хотите, или просто wrap_content.
Я просто отвечал на очень похожий вопрос по этому поводу, и случайно нашел его, когда искал ссылку для подтверждения своих утверждений, так что удачи вам:)
Мой другой ответ:
ViewPager не поддерживает wrap_content
поскольку он (обычно) никогда не загружает все свои дочерние элементы одновременно, и поэтому не может получить соответствующий размер (можно использовать пейджер, который меняет размер каждый раз, когда вы переключаете страницу).
Однако вы можете установить точный размер (например, 150dp) и match_parent
работает так же.
Вы также можете динамически изменять размеры из своего кода, изменяя height
-атрибут в его LayoutParams
,
Для ваших нужд вы можете создать ViewPager в своем собственном xml-файле с значением layout_height, равным 200dp, а затем в своем коде, вместо создания нового ViewPager с нуля, вы можете надуть этот xml-файл:
LayoutInflater inflater = context.getLayoutInflater();
inflater.inflate(R.layout.viewpagerxml, layout, true);
Используя ответ Daniel López Lacalle, я создал этот класс в Котлине. Надеюсь, это сэкономит вам больше времени
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)
}}
Я просто столкнулся с той же проблемой. У меня был ViewPager, и я хотел показать объявление на кнопке. Решение, которое я нашел, состояло в том, чтобы вставить пейджер в RelativeView и установить его layout_above с идентификатором представления, который я хочу видеть под ним. это сработало для меня.
вот мой макет 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>
Я уже сталкивался с этой проблемой в нескольких проектах и никогда не имел полного решения. Поэтому я создал проект github WrapContentViewPager как замену ViewPager на месте.
https://github.com/rnevet/WCViewPager
Решение было вдохновлено некоторыми ответами здесь, но улучшается:
- Динамически изменяет высоту ViewPager в соответствии с текущим View, в том числе при прокрутке.
- Принимает во внимание высоту "декора" просмотров, таких как PagerTabStrip.
- Принимает во внимание все Padding.
Обновлен для поддержки библиотеки версии 24, которая сломала предыдущую реализацию.
Я также столкнулся с этой проблемой, но в моем случае у меня был 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;
}
}
Я не уверен, что это лучший подход, хотел бы услышать комментарии, если вы считаете, что это хорошо / плохо / зло, но, похоже, в моей реализации это работает довольно хорошо:)
Надеюсь, это поможет кому-то там!
РЕДАКТИРОВАТЬ: я забыл добавить requestLayout()
после звонка super.measure()
(иначе это не перерисовывает представление).
Я также забыл добавить отступы родителя до конечной высоты.
Я также прекратил сохранять исходные параметры MeasureSpec ширины / высоты в пользу создания нового по мере необходимости. Обновили код соответствующим образом.
Еще одна проблема, с которой я столкнулся, заключалась в том, что в ScrollView
и обнаружил, что виновник измерял ребенка с MeasureSpec.EXACTLY
вместо MeasureSpec.UNSPECIFIED
, Обновлено, чтобы отразить это.
Все эти изменения были добавлены в код. Вы можете проверить историю, чтобы увидеть старые (неправильные) версии, если хотите.
Другое решение заключается в обновлении 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
просто регулярно ImageView
с переопределением 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
), каждое странное движение назад и вперед между двумя страницами.
Если вы используете статический контент внутри виджета, и вам не нужна необычная анимация, вы можете использовать следующий вид пейджер.
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));
}
}
Улучшенный ответ Даниэля Лопеса Лакалле, переписанный на Котлине:
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)
}
}
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 пользовательских ViewPager в ScrollView
, Верх из них измеряется на основе соотношения сторон, а остальные просто layout_height=wrap_content
, Я пробовал решения Cybergen, Daniel López Lacalle. Никто из них не работает полностью для меня.
Я думаю, что Cybergen не работает на странице> 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
высота основана на текущем активе ребенка. Мне это не нужно, но если вы предложите решение, я буду рад обновить ответ.
В случае, если вам нужен 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();
}
});
}
}
Исходя из исходного кода приложения Popcorn для Android, я нашел это решение, которое динамически регулирует размер видового пейджера с хорошей анимацией в зависимости от размера текущего потомка.
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);
}
}
Я столкнулся с той же проблемой, и мне также пришлось заставить ViewPager обернуть его содержимое, когда пользователь прокручивал страницы. Используя приведенный выше ответ Cybergen, я определил метод 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.
Этот ViewPager изменяет размер только до текущих видимых дочерних элементов (не самых больших из его фактических дочерних элементов)
Идея из /questions/2080840/android-viewpager-poluchit-tekuschij-vid/49371372#49371372
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;
}
}
Другой код Kotlin
class DynamicViewPager @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : ViewPager(context, attrs) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var height = 0
(0 until childCount).forEach {
val child = getChildAt(it)
child.measure(
widthMeasureSpec,
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
)
height = max(height, child.measuredHeight)
}
if (height > 0) {
super.onMeasure(
widthMeasureSpec,
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
)
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
}
}
Для людей, имеющих эту проблему и программирующих для Xamarin Android в C#, это также может быть быстрым решением:
pager.ChildViewAdded += (sender, e) => {
e.Child.Measure ((int)MeasureSpecMode.Unspecified, (int)MeasureSpecMode.Unspecified);
e.Parent.LayoutParameters.Height = e.Child.MeasuredHeight;
};
Это в основном полезно, если ваши детские взгляды имеют одинаковую высоту. В противном случае вам потребуется хранить какое-то значение "минимума" по всем дочерним элементам, с которыми вы проверяете, и даже в этом случае вы можете не захотеть видеть пустые места под вашими дочерними представлениями меньшего размера.
Однако само по себе решение не является достаточным для меня, но это потому, что мои дочерние элементы являются listViews, и их MeasuredHeight, похоже, вычисляется неправильно.
Я редактирую ответ cybergen, чтобы заставить viewpager изменять высоту в зависимости от выбранного элемента.Класс такой же, как у cybergen, но я добавил вектор целых чисел, который является высотой всех дочерних просмотров 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"/>
При необходимости исправьте мой английский
Код ниже - единственное, что у меня сработало.
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);
У меня есть версия WrapContentHeightViewPager, которая работала правильно до API 23, которая будет изменять размер базы высоты родительского представления в текущем выбранном дочернем представлении.
После обновления до API 23 он перестал работать. Оказывается, старое решение использовало getChildAt(getCurrentItem())
чтобы получить текущее дочернее представление для измерения, которое не работает. Смотрите решение здесь: /questions/14104578/kak-poluchit-dochernij-vid-viewpager-dlya-dannogo-elementa/14104589#14104589
Ниже работает с 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);
}
В моем случае мне понадобился viewpager с wrap_content для текущего выбранного элемента и анимации при применении размера. Ниже вы можете увидеть мою реализацию. Может кому пригодится.
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" />
Большинство решений, которые я здесь вижу, похоже, проводят двойное измерение: сначала измеряют дочерние представления, а затем вызывают super.onMeasure()
Я придумал обычай WrapContentViewPager
это более эффективно, хорошо работает с RecyclerView и Fragment
Вы можете проверить демо здесь:
https://github.com/ssynhtn/WrapContentViewPager
и код класса здесь: WrapContentViewPager.java
Вы можете переключиться на ViewPager2. Это обновленная версия ViewPager. Он делает то же самое, что и ViewPager, но более умным и эффективным способом. ViewPager2 поставляется с множеством новых функций. Конечно, проблема с переносом содержимого была решена ViewPager2.
Из документов Android: "ViewPager2 заменяет ViewPager, решая большинство проблем своего предшественника, включая поддержку макета справа налево, вертикальную ориентацию, изменяемые коллекции фрагментов и т. Д."
Я рекомендую эту статью новичкам:
https://medium.com/google-developer-experts/exploring-the-view-pager-2-86dbce06ff71
Измерьте высоту 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;
}
}
вызов setPrimaryView(Просмотр) :
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);
}
}
Если 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);
}
}
У меня похожий (но более сложный сценарий). У меня есть диалог, который содержит 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://stackru.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://stackru.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://stackru.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 не будет прокручиваться, когда высота диалогового окна будет достаточно короткой, чтобы этого потребовать.
Не все ответы работают идеально. Итак, я создал один. Ниже класса будет запрашиваться макет, когда новая страница будет выбрана для созданияviewPager
Высота - это высота текущего дочернего представления.
class WrapContentViewPager : ViewPager {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
private var curPos = 0
init {
addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {}
override fun onPageSelected(position: Int) {
curPos = position
requestLayout()
}
})
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
if (childCount == 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
return
}
measureChildren(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(measuredWidth, getChildAt(curPos).measuredHeight)
}
}
Для тех, кому нужно решение ViewPager2, в котором ViewPager2 будет иметь ту же высоту, что и максимальная высота всех его страниц, к сожалению, я нашел только этот обходной путь:
viewPager.doOnPreDraw {
//workaround to set the viewPagerheight the same as its children
var height = 0
for (i in 0 until featuresViewPager.adapter!!.itemCount) {
val viewHolder = viewPager.adapter!!.createViewHolder(viewPager, 0)
viewPager.adapter!!.bindViewHolder(viewHolder, i)
val child: View = viewHolder.itemView
child.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(viewPager.width, View.MeasureSpec.EXACTLY)
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
child.measure(widthMeasureSpec, heightMeasureSpec)
val childHeight = child.measuredHeight
child.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
if (childHeight > height)
height = childHeight
}
viewPager.layoutParams.height = height
}
Я говорю "грустно", потому что он просматривает все страницы, создает их представления, измеряет их и по пути вызывает функции, предназначенные для других целей.
В большинстве случаев должно работать нормально.
Пожалуйста, дайте мне знать, если вы знаете более хорошее решение.