我已经设置了一个简单的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添加到寻呼机的布局?
我有一个版本的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);
}
我的答案基于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。
我只是在回答一个非常类似的问题,在寻找一个链接来支持我的说法时,碰巧发现了这个,你很幸运:)
我的另一个回答是:
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);
我有一个类似的(但更复杂的场景)。我有一个对话框,其中包含一个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无法滚动。
从爆米花时间安卓应用程序的源代码,我发现这个解决方案,动态调整大小的viewpager与漂亮的动画取决于当前的孩子的大小。
https://git.popcorntime.io/popcorntime/android/blob/5934f8d0c8fed39af213af4512272d12d2efb6a6/mobile/src/main/java/pct/droid/widget/WrappingViewPager.java
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);
}
}