我已经设置了一个简单的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添加到寻呼机的布局?
另一个更通用的解决方案是让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;
}
}
重写你的ViewPager的onMeasure如下将使它获得它目前拥有的最大子节点的高度。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = 0;
int childWidthSpec = MeasureSpec.makeMeasureSpec(
Math.max(0, MeasureSpec.getSize(widthMeasureSpec) -
getPaddingLeft() - getPaddingRight()),
MeasureSpec.getMode(widthMeasureSpec)
);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(childWidthSpec, MeasureSpec.UNSPECIFIED);
int h = child.getMeasuredHeight();
if (h > height) height = h;
}
if (height != 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
改进丹尼尔López Lacalle回答,重写在Kotlin:
class MyViewPager(context: Context, attrs: AttributeSet): ViewPager(context, attrs) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val zeroHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
val maxHeight = children
.map { it.measure(widthMeasureSpec, zeroHeight); it.measuredHeight }
.max() ?: 0
if (maxHeight > 0) {
val maxHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY)
super.onMeasure(widthMeasureSpec, maxHeightSpec)
return
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
}
我编辑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"/>
如有必要请纠正我的英语
我只是在回答一个非常类似的问题,在寻找一个链接来支持我的说法时,碰巧发现了这个,你很幸运:)
我的另一个回答是:
ViewPager不支持wrap_content,因为它(通常)不会同时加载所有的子页面,因此无法获得适当的大小(可以选择在每次切换页面时改变大小)。
然而,你可以设置一个精确的尺寸(例如150dp)和match_parent也可以。
您还可以通过更改其LayoutParams中的height-属性,从代码中动态修改维度。
根据你的需要,你可以在它自己的xml文件中创建ViewPager, layout_height设置为200dp,然后在你的代码中,而不是从头创建一个新的ViewPager,你可以扩展这个xml文件:
LayoutInflater inflater = context.getLayoutInflater();
inflater.inflate(R.layout.viewpagerxml, layout, true);
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);
}