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

今天遇到一个坑爹的需求,需求如题,就是想实现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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值