現(xiàn)象:
viewpager 嵌套多個fragment违寿。fragment中有l(wèi)istview湃交。在切換的時時候,大家會遇到這種情況藤巢,listview的內(nèi)容有多有少搞莺,但是viewpager都是以最高的listview的位置,這樣就會導致高度比較小向上滑動的時候會出現(xiàn)空白掂咒。
遇到這問題才沧,首先會想到的是ViewPager高度沒有自動適配listview。那么我們首先可以根據(jù)獲取fragment中l(wèi)istview的高度動態(tài)設置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中出現(xiàn)大面積空白。所以綜上所述饱搏,我們要達到的效果是去除這空白非剃,使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的高度深浮。好了如果你按照這個邏輯去做已經(jīng)很接近實現(xiàn)了,但要說明一個問題眠冈,很重要的一個問題飞苇,在低版本的SDK下菌瘫,似乎沒什么問題,但是在高版本SDK下布卡,就有了問題雨让。這個問題糾結了我一天多,因為我在Android4.3的手機忿等,完全實現(xiàn)了栖忠,但是在隊友Android6.0的手機下就出現(xiàn)了問題。(這是因為高版本中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);
}
```
簡單粗暴的辦法襟诸,用版本控制你要加載的頁面高度,親測可以解決基协,但是并不知道到了哪個高版本出現(xiàn)了變化歌亲,我這里用的是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