现象:
viewpager 嵌套多个fragment。fragment中有listview。在切换的时时候,大家会遇到这种情况,listview的内容有多有少,但是viewpager都是以最高的listview的位置,这样就会导致高度比较小向上滑动的时候会出现空白。
遇到这问题,首先会想到的是ViewPager高度没有自动适配listview。那么我们首先可以根据获取fragment中listview的高度动态设置viewPager高度。
第一步自定义ViewPager。
```
/**
* 自动适应高度的ViewPager
* @author
*
*/
public class CustomViewPager extendsViewPager {
publicCustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(intwidthMeasureSpec,intheightMeasureSpec) {
intheight =0;
for(inti =0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
inth = child.getMeasuredHeight();
if(h > height)
height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
```
这是最常见的一种处理办法,选择你fragment中高度最大的那个作为你整个viewpager的高度。解决了冲突问题,但你会遇到这样一个棘手的问题:所有viewpager中的fragment都是那个最大的高度,如果你的fragment中view的高度很小的话,或者view的高度过大的话,会导致自身或者其他fragment中出现大面积空白。所以综上所述,我们要达到的效果是去除这空白,使viewpager的高度真正“自适应”。所以进一步升级 viewpager的自定义
升级版的viewpager
```
importandroid.content.Context;
importandroid.support.v4.app.Fragment;
importandroid.support.v4.view.ViewPager;
importandroid.util.AttributeSet;
importandroid.util.TypedValue;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.widget.LinearLayout;
importjava.util.HashMap;
importjava.util.Map;
/**
* Created by vipui on 16/8/25.
*/
public class CustomViewpager extends ViewPager {
private int current;
privateintheight =0;
privatebooleanscrollble =true;
publicCustomViewpager(Context context) {
super(context);
}
publicCustomViewpager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec) {
if(getChildCount() > current) {
View child = getChildAt(current);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
inth = child.getMeasuredHeight();
height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
publicvoidresetHeight(intcurrent) {
this.current = current;
if(getChildCount() > current) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
if(layoutParams ==null) {
layoutParams =newLinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height);
}else{
layoutParams.height = height;
}
setLayoutParams(layoutParams);
}
}
@Override
publicbooleanonTouchEvent(MotionEvent ev) {
if(!scrollble) {
returntrue;
}
returnsuper.onTouchEvent(ev);
}
publicbooleanisScrollble() {
returnscrollble;
}
publicvoidsetScrollble(booleanscrollble) {
this.scrollble = scrollble;
}
}
```
onMeasure()测量控件的方法,resetHeight()重置viewpager的高度的方法,从代码中可以看出在调用resetHeight()方法中传入实参current后,viewpager的高度会变成你传入实参对应下标的fragment的高度,那么在哪里调用这个方法呢?请看代码:
```
viewpager.setOnPageChangeListener(newViewPager.OnPageChangeListener() {
@Override
publicvoidonPageScrolled(intposition,floatpositionOffset,intpositionOffsetPixels) {
}
@Override
publicvoidonPageSelected(intposition) {
viewpager.resetHeight(position);
}
}
@Override
publicvoidonPageScrollStateChanged(intstate) {
}
});
activityScdetailsBottomVp.resetHeight(0);
}
```
在viewpager中的onpagerChagelistener的方法中,当你改变viewpager的pager页位置时重置viewpager的高度。好了如果你按照这个逻辑去做已经很接近实现了,但要说明一个问题,很重要的一个问题,在低版本的SDK下,似乎没什么问题,但是在高版本SDK下,就有了问题。这个问题纠结了我一天多,因为我在Android4.3的手机,完全实现了,但是在队友Android6.0的手机下就出现了问题。(这是因为高版本中viewpager有改动,并不知道有什么改动,觉得是预加载的改动)对的,高度不对应,就是你viewpager中的fragment不是自己本身的高度,可能是其他fragment的高度,这个问题,大家都应该想的到,viewpager的预加载导致的(3个或3个以上的子view),viewpager在加载当前fragment的过程中会预加载临近两个的fragment,所以,拿viewpager中有三个fragment来说,你的第一个fragment的高度是第三个fragment的高度,(因为预加载到第三个)第一你们第二个fragment的高度是你 第一个fragment的高度(预加载到第一个),以此类推。解决这个问题的方法有两个,
第一个办法:
```
activityScdetailsBottomVp.setOnPageChangeListener(newViewPager.OnPageChangeListener() {
@Override
publicvoidonPageScrolled(intposition,floatpositionOffset,intpositionOffsetPixels) {
}
@Override
publicvoidonPageSelected(intposition) {
if(position ==0) {
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
activityScdetailsBottomVp.resetHeight(1);
}else{
activityScdetailsBottomVp.resetHeight(0);
}
}elseif(position ==1) {
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
activityScdetailsBottomVp.resetHeight(2);
}else{
activityScdetailsBottomVp.resetHeight(1);
}
}else{
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
activityScdetailsBottomVp.resetHeight(0);
}else{
activityScdetailsBottomVp.resetHeight(2);
}
}
}
@Override
publicvoidonPageScrollStateChanged(intstate) {
}
});
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
activityScdetailsBottomVp.resetHeight(1);
}else{
activityScdetailsBottomVp.resetHeight(0);
}
```
简单粗暴的办法,用版本控制你要加载的页面高度,亲测可以解决,但是并不知道到了哪个高版本出现了变化,我这里用的是KITKAT(android4.4),这个临界点有待商量。那么是否会有更好的办法解决呢,这就得考虑第二种办法了,也是我使用的办法,也是自定义viewpager,大致差不多,不过也修改了不少。
第二种:
```
importandroid.content.Context;
importandroid.os.Build;
importandroid.support.v4.app.Fragment;
importandroid.support.v4.view.ViewPager;
importandroid.util.AttributeSet;
importandroid.util.TypedValue;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.widget.LinearLayout;
importjava.util.ArrayList;
importjava.util.HashMap;
importjava.util.LinkedHashMap;
importjava.util.List;
importjava.util.Map;
/**
* Created by vipui on 16/8/25.
*/
publicclassCustomViewpagerextendsViewPager {
privateintcurrent;
privateintheight =0;
/**
* 保存position与对于的View
*/
privateHashMap mChildrenViews =newLinkedHashMap();
privatebooleanscrollble =true;
publicCustomViewpager(Context context) {
super(context);
}
publicCustomViewpager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec) {
if(mChildrenViews.size() > current) {
View child = mChildrenViews.get(current);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
height = child.getMeasuredHeight();
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
publicvoidresetHeight(intcurrent) {
this.current = current;
if(mChildrenViews.size() > current) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
if(layoutParams ==null) {
layoutParams =newLinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height);
}else{
layoutParams.height = height;
}
setLayoutParams(layoutParams);
}
}
/**
* 保存position与对于的View
*/
publicvoidsetObjectForPosition(View view,intposition)
{
mChildrenViews.put(position, view);
}
@Override
publicbooleanonTouchEvent(MotionEvent ev) {
if(!scrollble) {
returntrue;
}
returnsuper.onTouchEvent(ev);
}
publicbooleanisScrollble() {
returnscrollble;
}
publicvoidsetScrollble(booleanscrollble) {
this.scrollble = scrollble;
}
}
```
setObjectForPosition()方法中是为了调用存放你的view和他对应的position,这个是参考了鸿洋大神的一篇文章
setObjectForPosition()这个方法了,请看我的一个fragment
```
private CustomViewpager vp;
private int index;
publicSecurityInfoFragment(CustomViewpager vp,int index) {
this.vp = vp;
}
@Nullable
@Override
publicView onCreateView(LayoutInflater inflater,@NullableViewGroup container,@NullableBundle savedInstanceState) {
View view = inflater.inflate(R.layout.xxxxx,null);
vp.setObjectForPosition(view,index);
return view;
}
```
最后一步,就是调用resetheight()方法了,请look代码
```
activityScdetailsBottomVp.setOnPageChangeListener(newViewPager.OnPageChangeListener() {
@Override
publicvoidonPageScrolled(intposition,floatpositionOffset,intpositionOffsetPixels) {
}
@Override
publicvoidonPageSelected(intposition) {
activityScdetailsBottomVp.resetHeight(position);//这句很重要
}
@Override
publicvoidonPageScrollStateChanged(intstate) {
}
});
activityScdetailsBottomVp.resetHeight(0);//这句更重要
}
```
最后,特别声明,本编参考了大神们的博客:
参考了鸿洋大神的一篇文章:http://blog.csdn.net/lmj623565791/article/details/38026503