ViewPager控件,我以前的做法都是直接写死高度,这个对于做广告滚动图的还可以,因为高度都是16:9,写死也没关系。但是对于显示那些动态数据列表,这明显已经不满足需求了。
其他大部分控件的的自适应都是用这个wrap_content属性就可以搞定,但是对于ViewPager来说没有效果,不设置高度就不显示处理,我想到的解决办法就是重写onMeasure方法。
原理是:获取子控件的高度用于重新设置ViewPager的高度
第一种做法是:遍历ViewPager的子控件,获取子控件最大的height值做完ViewPager的高度,不过这种容易导致数据少的View底部有一大块空白,明显不满足友好的用户体验
@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);
}
第二种做法是:根据子控件的索引,直接获取当前显示子控件的高度,重设ViewPager的高度。具体代码如下
public class WrapContentHeightViewPager extends ViewPager {
private int currentIndex;
private int height = 0;
//保存view对应的索引
private HashMap mChildrenViews = new LinkedHashMap();
public WrapContentHeightViewPager(Context context) {
super(context);
}
public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mChildrenViews.size() > currentIndex) {
View child = mChildrenViews.get(currentIndex);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
height = child.getMeasuredHeight();
}
if (mChildrenViews.size() != 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 重新设置高度
*
* @param current
*/
public void resetHeight(int current) {
currentIndex = current;
if (mChildrenViews.size() > current) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
if (layoutParams == null) {
layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height);
} else {
layoutParams.height = height;
}
setLayoutParams(layoutParams);
}
}
/**
* 保存View对应的索引,需要自适应高度的一定要设置这个
*/
public void setViewForPosition(View view, int position) {
mChildrenViews.put(position, view);
}
}
用法是:实现ViewPager的 滑动监听事件,滑动之后根据当前子控件的索引,传入进去重新设置高度,开始需要一个默认的
private void initViewPagerListener() {
mViewPagerView.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
mViewPagerView.resetHeight(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
mViewPagerView.resetHeight(0);
}
其实上面resetHeight 里面的高度是啥都没所谓,因为执行setLayoutParams 就会触发onMeasure方法,onMeasure方法里面就有获取当前子控件高度,重新设置给ViewPager的处理
特别注意:需要自适应高度的一定要在你的子控件里面,子控件里面,子控件里面设置setViewForPosition
/**
* 用于自适应高度
* @param mViewPagerView
*/
public void setWrapContentHeightViewPager(WrapContentHeightViewPager mViewPagerView) {
mViewPagerView.setViewForPosition(this,0);
}
介于很多人不明白子控件,现补充下面内容:
ViewPager的数据我这里传入的是List
我所说的子控件就是你自己的自定义的View,在你自定义的View里面设置setWrapContentHeightViewPager这个方法,然后把ViewPager传入进去,设置当前自定义View的索引