我已经设置了一个简单的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添加到寻呼机的布局?
我遇到了同样的问题,当用户在页面之间滚动时,我还必须让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显示的当前页面的高度。
我的答案基于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。
我有一个版本的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);
}
如果你正在使用的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的measuredHeight,如果找到一个。否则,即使它占用了额外的空间,它的高度也不会被认为是所有子结点的最大高度。
希望这能帮助到其他人。很抱歉,这有点黑…
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,如果ScrollView内容不需要对话框可用的全部高度,那么该页面(以及整个对话框)应该WRAP_CONTENT。
现有的答案都不完全适用于这个特定的场景。坚持住,这是一段颠簸的旅程。
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无法滚动。