ViewPager的高度根据item的高度自适应

来源:互联网 发布:供销社大数据集团 地点 编辑:程序博客网 时间:2024/06/05 06:35

今天遇到一个坑爹的需求,需求如题,就是想实现ViewPager在切换的时候自身的高度随itemView的高度调整. 
使用过ViewPager的人都知道,即使你在布局中写的高度是wrap_content,但是运行起来就会发现他其实是match_parent的效果,也就是填充整个屏幕,除非你写死一个高度. 
解决这个问题,那就只能自定义一个View继承ViewPager然后重写onMeasure方法了.在onMeasure方法中获取当前正在展示的子View,然后测量其高度.然后再调用ViewPager的setMeasuredDimension方法设置高度的时候先判断当前ViewPager是否是match_parent,如果时则使用ViewPager测量的高度,否则就取子View和ViewPager各自测量高度的最小值.

那么问题来了.onMeasure方法如果才能获取到当前正在展示的子View呢? 
通过getCurrentItem()可以获取当前正在展示的子View的position,然后再调用getChildAt(int position)就可以获取到该子View了.

好了,新问题来了.onMeasure方法通常只会回调2次,而且再以后的子View切换的时候并不会再回调onMeasure,该怎么解决呢? 
哈,我的第一反应就是手动刷新onMeasure()方法,刚好requestLayout()方法可以帮我实现,调用这个方法,它不但可以帮我们调用onMeasure()还会调用layout()方法.那么,我只需要监听ViewPager的滑动切换就好了, 然后在 
onPageSelected()方法中去调用requestLayout()方法即可,完美!

看看效果图: 
这里写图片描述

展示了2张图,高度不一样,下面的红色区域代表其他布局.ViewPager会随着图片的高度自适应,下面红色区域也会随着调整.

来看看代码:

/** * 根据View的内容自动适应高度的ViewPager * Created by mChenys on 2017/1/11. */public class AutofitViewPager extends ViewPager {    private static final String TAG = "AutofitViewPager";    public AutofitViewPager(Context context) {        this(context,null);    }    public AutofitViewPager(Context context, AttributeSet attrs) {        super(context, attrs);        addOnPageChangeListener(new OnPageChangeListener() {            @Override            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {            }            @Override            public void onPageSelected(int position) {                requestLayout();            }            @Override            public void onPageScrollStateChanged(int state) {            }        });    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        Log.d(TAG, "onMeasure");        // find the current child view        View view = getChildAt(getCurrentItem());        if (view != null) {            // measure the current child view with the specified measure spec            view.measure(widthMeasureSpec, heightMeasureSpec);        }        setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, view));    }    /**     * Determines the height of this view     *     * @param measureSpec A measureSpec packed into an int     * @param view the base view with already measured height     *     * @return The height of the view, honoring constraints from measureSpec     */    private int measureHeight(int measureSpec, View view) {        int result = 0;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        if (specMode == MeasureSpec.EXACTLY) {            result = specSize;        } else {            // set the height from the base view if available            if (view != null) {                result = view.getMeasuredHeight();            }            if (specMode == MeasureSpec.AT_MOST) {                result = Math.min(result, specSize);            }        }        return result;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

测试类

public class TestViewPager extends AppCompatActivity {    private ViewPager mViewPager;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_vp);        mViewPager = (ViewPager) findViewById(R.id.vp);        mViewPager.setAdapter(new PagerAdapter() {            @Override            public int getCount() {                return 2;            }            @Override            public boolean isViewFromObject(View view, Object object) {                return view == object;            }            @Override            public Object instantiateItem(ViewGroup container, int position) {                View view = null;                if (position == 0) {                    view = View.inflate(getBaseContext(),R.layout.item_vp1,null);                } else if (position == 1) {                    view = View.inflate(getBaseContext(),R.layout.item_vp2,null);                }                container.addView(view);                return view;            }            @Override            public void destroyItem(ViewGroup container, int position, Object object) {                container.removeView((View) object);            }        });    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

测试类布局

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:orientation="vertical">    <mchenys.net.csdn.blog.AutofitViewPager        android:id="@+id/vp"        android:layout_width="match_parent"        android:layout_height="wrap_content"/>    <FrameLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:background="@color/colorAccent"></FrameLayout></LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在使用过程中发现bug,当ViewPager滑动的position大于3时,AutofitViewPager 代码的第36行获取itemView的时候返回null.

 View view = getChildAt(getCurrentItem());
  • 1
  • 1

导致后面获取的测量高度永远都是0.猜测是viewpager里面最多有3个view,超过三个的时候就把旧的回收了,解决这个问题也挺巧妙的,就是在AutofitViewPager 设置adapter的时候在 instantiateItem方法中给返回的itemView设置一个id,该id就赋值为当前的position,然后再将AutofitViewPager 类的第36行代码替换为:

View view = findViewById(getCurrentItem());
  • 1
  • 1

这样就解决了.

最后再提醒下,在显示网络图片的时候ImageView最好是在布局中实例化出来的,而不要直接在instantiateItem方法内通过new来创建,我试过用Glide库加载网络图片通过new来创建ImageView设置的LayoutParams宽高信息后,AutofitViewPager去获取ImageView的测量高度永远都是0,什么也看不到.如果是本地图片则不会有影响.

文章来自:http://blog.csdn.net/mchenys/article/details/54353136

阅读全文
0 0
原创粉丝点击