之前项目遇到了一个需求,需要scrollview里面嵌套一个viewpager,而且要做到切换viewpager的时候恰好完全展示对应的page。
在网上搜索了很久,只看到某位大神提供的CustomViewPager,代码如下:
public class CustomViewPager extends ViewPager {
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int 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.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
研究一番后发现,onMeasure方法中最后记录的是所有page多次测量后高度中的最大值,这样两个高度不一的page虽然都能完全展示,但是高度矮的那个就会有留白,不符合我们恰好展示的需求。
于是我对代码进行了修改,有了SelfAdaptingViewPager,代码如下:
public class SelfAdaptingViewPager extends ViewPager {
private Map<Integer, Integer> map = new HashMap<>(2);
private int currentPosition = 0;
private MarginLayoutParams params;
public SelfAdaptingViewPager(Context context) {
super(context);
}
public SelfAdaptingViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
//在View的绘制过程中此方法会被多次调用,每次都会测量出新的数据,我们用最新的数据替换旧的数据
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
Log.d("SelfAdaptingViewPager", "measuredHeight=" + h);
Log.d("SelfAdaptingViewPager", "getChildCount=" + getChildCount());
Log.d("SelfAdaptingViewPager", "getChildAt(i)=" + i);
/* Log.d("SelfAdaptingViewPager", "getChildAt(i).getId())=" + getChildAt(i).getId());
if (Build.VERSION.SDK_INT > 19) {
map.put(Math.abs(i - 1), h);
} else {*/
map.put(i, h);
// }
}
int height = 0;
//onMeasure第一次被调用的时候,遍历不到child(View的绘制流程只是了解最基本的,我就不解释了)
height = map.get(currentPosition) == null ? 0 : map.get(currentPosition);
Log.d("SelfAdaptingViewPager", "height=" + height);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
//在切换tab的时候,重置ViewPager的高度
public void resetHeight(int currentPosition) {
this.currentPosition = currentPosition;
params = (MarginLayoutParams) getLayoutParams();
if (params == null) {
params = new MarginLayoutParams(LayoutParams.MATCH_PARENT, map.get(currentPosition));
} else {
params.height = map.get(currentPosition);
}
setLayoutParams(params);
}
}
但是我在用android4.4的机型测试时发现了问题,一个page留白,一个page不能完全展示,显然两个page所展示的高度正好弄反了。
后来发现是和手机的android版本有关,使用android版本4.4及以下的机型都有这个问题。遍历viewpager的儿子,得到的第一个儿子是第二个page对应的view,得到的第二儿子是第一个page对应的view。
这次周末在家我想再研究一下这个问题,结果自己写的demo在任何机型上并不会出现这样的问题。
最终发现问题的出现和依赖的v7包有关系,我在工作项目中依赖的是com.android.support:appcompat-v7:23.4.0,demo中依赖的是com.android.support:appcompat-v7:23.1.1,所以可能是不同版本的v7包所包含的v4包中ViewPager对于getChildAt的实现有区别吧,以后有时间和基础了我想我会进一步研究的。
下面是demo的截图: