我已经设置了一个简单的ViewPager,它在每个页面上都有一个高度为200dp的ImageView。
这是我的寻呼机:
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,但即使imageview只有200dp,分页器也总是充满屏幕。我尝试用“200”替换寻呼机的高度,但在不同分辨率下会得到不同的结果。我无法将“dp”添加到该值。如何将200dp添加到寻呼机的布局?
另一种解决方案是根据PagerAdapter中的当前页面高度更新ViewPager高度。假设你用这种方式创建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是动态添加到PagerAdapter的PageInfo结构的内部列表,CustomImageView只是常规的ImageView,覆盖onMeasure()方法,根据指定的宽度设置其高度并保持图像纵横比。
你可以在setPrimaryItem()方法中强制ViewPager的高度:
@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)));
}
注意数学。这修复了ViewPager不更新显示的页面(显示为空白),当前一页的高度为零时(即CustomImageView中可绘制的空),在两页之间每次奇怪的来回滑动。
我有一个版本的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);
}
另一个更通用的解决方案是让wrap_content正常工作。
我已经扩展了ViewPager来覆盖onMeasure()。高度被包裹在第一个子视图周围。如果子视图的高度不完全相同,这可能会导致意想不到的结果。因此,类可以很容易地扩展,让我们说动画到当前视图/页面的大小。但我不需要。
你可以在yout XML布局中使用这个ViewPager,就像原来的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"/>
优点:这种方法允许在任何布局(包括RelativeLayout)中使用ViewPager来覆盖其他ui元素。
还有一个缺点:如果你想使用页边距,你必须创建两个嵌套的布局,并给内部的一个指定所需的页边距。
代码如下:
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;
}
}
我的答案基于Daniel López Lacalle和这篇文章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。
另一种解决方案是根据PagerAdapter中的当前页面高度更新ViewPager高度。假设你用这种方式创建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是动态添加到PagerAdapter的PageInfo结构的内部列表,CustomImageView只是常规的ImageView,覆盖onMeasure()方法,根据指定的宽度设置其高度并保持图像纵横比。
你可以在setPrimaryItem()方法中强制ViewPager的高度:
@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)));
}
注意数学。这修复了ViewPager不更新显示的页面(显示为空白),当前一页的高度为零时(即CustomImageView中可绘制的空),在两页之间每次奇怪的来回滑动。
给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