我已经设置了一个简单的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添加到寻呼机的布局?
对于有此问题并使用c#编写Xamarin Android代码的人来说,这可能也是一个快速解决方案:
pager.ChildViewAdded += (sender, e) => {
e.Child.Measure ((int)MeasureSpecMode.Unspecified, (int)MeasureSpecMode.Unspecified);
e.Parent.LayoutParameters.Height = e.Child.MeasuredHeight;
};
如果您的子视图具有相同的高度,这主要是有用的。否则,您将被要求在您检查的所有子视图上存储某种“minimumHeight”值,即使这样,您可能也不希望在较小的子视图下显示空白区域。
虽然解决方案本身对我来说是不够的,但这是因为我的子项目是listViews,他们的MeasuredHeight似乎没有正确计算。
下面的代码是唯一对我有用的东西
1. 使用这个类来声明一个hightwrappingviewpager:
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);
并非所有答案都完美无缺。所以我创建了一个。下面的类将请求布局时,一个新的页面被选择,使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)
}
}
我只是在回答一个非常类似的问题,在寻找一个链接来支持我的说法时,碰巧发现了这个,你很幸运:)
我的另一个回答是:
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);
以上建议对我都没用。我的用例是在ScrollView中有4个自定义ViewPagers。它们的顶部是基于纵横比测量的,其余的只是layout_height=wrap_content。我试过cybergen, Daniel López Lacalle解决方案。没有一个是完全适合我的。
我猜为什么cybergen不能在页面> 1上工作是因为它基于第1页计算寻呼机的高度,如果你进一步滚动,这是隐藏的。
cybergen和Daniel López Lacalle建议在我的情况下都有奇怪的行为:3个中的2个加载ok, 1个随机高度为0。似乎onMeasure是在儿童被填充之前被调用的。所以我想出了这两个答案加上我自己的修正:
@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的布局参数中保存计算出的第一页高度。不要忘记将fragment的布局高度设置为wrap_content,否则你可以得到height=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的高度。我不需要它,但如果你提出解决方案,我很乐意更新答案。
另一个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)
}
}
}