美文网首页高级UI
ViewPager 的使用

ViewPager 的使用

作者: wind_sky | 来源:发表于2019-08-06 16:50 被阅读2次

一. 简介

ViewPager 是support v4 包提供的控件,可以实现一组View 切换显示的效果。

二. 使用

使用ViewPager 也比较简单,主要有以下几步:

  1. 获取ViewPager 实例,包括从XML 或直接new

  2. 自定义ViewPager 的adapter,并为ViewPager 设置adapter

  3. 设置ViewPager 的切换效果(可选)

  4. 设置ViewPager 的事件监听(可选)

三. 自定义Adapter

ViewPager 的adapter 类型为PagerAdapter,它是一个抽象类,support 包提供了两种实现,一个FragmentPagerAdapter,另一个是FragmentStatePagerAdapter,这两个Adapter 是ViewPager 和Fragment 结合使用时所用,稍后介绍。

现在介绍一下,继承PagerAdapter 的自定义Adapter。继承PagerAdapter 必须要重写两个方法,即 getCount、isViewFromObject,同时为了保证功能的实现,还应该重写instantiateItem 和 destroyItem 方法,示例代码如下:

    private class MyPagerAdapter extends PagerAdapter { 

        private List<Uri> data; 

        MyPagerAdapter(List<Uri> data) { 
            this.data = data; 
        } 

        @Override 
        public Object instantiateItem(ViewGroup container, int position) { 
            // 创建 Item, 本例为显示图片
            View view = LayoutInflater.from(container.getContext()).inflate(R.layout.item_two, container, false); 
            ImageView image = view.findViewById(R.id.show_image_iv); 

            Picasso.with(container.getContext()) 
                    .load(data.get(position)) 
                    .into(image); 

            container.addView(view); 
            return view; 
        } 

        @Override 
        public void destroyItem(ViewGroup container, int position, Object object) { 

            if (object instanceof View) { 
                container.removeView((View)object); 
            } 
        } 

        @Override 
        public int getItemPosition(Object object) { 
            return POSITION_UNCHANGED;     // 默认返回,与刷新有关
        } 

        @Override 
        public boolean isViewFromObject(View view, Object object) { 
            return object == view; 
        } 

        @Override 
        public int getCount() { 
            return data.size(); 
        } 

        public void setData(List<Uri> data) { 
            this.data = data; 
            notifyDataSetChanged(); 
        } 
    }

效果:


pager.gif

PagerAdapter 工作流程:

PagerAdapter 作为 ViewPager 的适配器,无论 ViewPager 有多少页,PagerAdapter 在初始化时也只初始化开始的2个 View,即调用2次instantiateItem 方法。而接下来每当 ViewPager 滑动时,PagerAdapter 都会调用 destroyItem 方法将距离该页2个步幅以上的那个 View 销毁,以此保证 PagerAdapter 最多只管辖3个 View,且当前 View 是3个中的中间一个,如果当前 View 缺少两边的 View,那么就 instantiateItem,如里有超过2个步幅的就 destroyItem。

在实现Adapter 时,有几点需要注意,可能会出坑。

  • 第一个就是destroyItem 方法,如果使用 container.removeViewAt(index) 方法,可能会导致出现 越界 crash,所以应该使用removeView(object) 方法。

  • 第二个是getItemPosition 方法,默认的返回值是 POSITION_UNCHANGED,还有另外一种值是 POSITION_NONE,这两个值的区别在于刷新数据时,

Viewpager 的刷新过程是这样的,在每次调用 PagerAdapter 的 notifyDataSetChanged() 方法时,都会激活 getItemPosition(Object object) 方法,该方法会遍历 ViewPager 的所有 Item(由缓存的 Item 数量决定,默认为当前页和其左右加起来共3页,这个可以自行设定,但是至少会缓存2页),为每个 Item 返回一个状态值(POSITION_NONE/POSITION_UNCHANGED),

如果是 POSITION_NONE,那么该 Item 会被 destroyItem(ViewGroup container, int position, Object object) 方法 remove 掉,然后重新加载,

如果是 POSITION_UNCHANGED,就不会重新加载,默认是 POSITION_UNCHANGED,所以如果不重写 getItemPosition(Object object),修改返回值,就无法看到 notifyDataSetChanged() 的刷新效果。

注:当ViewPager 的Item 是Fragment 时,系统提供了FragmentPagerAdapter 和 FragmentStatePagerAdapter,但是动态修改时可能还是会有一些问题,需要进行一些特殊处理。

四. 切换效果

有时候我们可能觉得默认的切换太平淡,想来点花哨的效果,Android 提供了一个接口 PageTransformer,实现这个接口,并为ViewPager 设置Transformer 即可实现切换特效。

PageTransformer 接口只有一个方法,即 public void transformPage(View page, float position) ,它有两个参数,

page表示 ViewPager 中的一页,

position表示page当前的位置,[-1, 0)表示屏幕左边的page(部分可见),[0, 0]表示屏幕上的page(完全可见),(0, 1]表示屏幕右边的page(部分可见),具体看下图:

image.png

page向左边滑动时,position从0向-1变化,当position==-1时完全不可见;当page向右滑动时,position从0向1变化,当position==1时完全不可见。这样,我们根据position 的变化来对View 进行相关的操作,比如旋转、缩放、透明度等,就可以实现不同的切换效果。如下例,

private class GalleryTransformer implements ViewPager.PageTransformer { 
        private static final float MAX_ROTATION = 20.0F; 
        private static final float MIN_SCALE = 0.75f; 
        private static final float MAX_TRANSLATE = 20.0F; 

        private int width = UiUtils.getScreenWidth(ViewActivity.this); 
        private boolean firstPage;      // 是否第一页 
        private boolean firstTime = true;   // 是否第一遍执行 
        private int count;      // 执行次数, 与setOffscreenPageLimit 有关 

        GalleryTransformer() { 
            firstPage = true; 
        } 

        // 第一页和第一遍的逻辑,用于处理一页显示多个Item 时,初始状态第二页的状态 

        @Override 
        public void transformPage(View page, float position) { 
            if (count++ == 3) { 
                firstTime = false;      // 第一遍执行完 
            } 

            float offset = (float)(width - page.getWidth()) / 2;        // 因为一页显示多个Item 
            if (page.getWidth() != 0) { 
                position = position - offset / page.getWidth();         // 所以要对position 进行相关偏移,否则将显示异常 
            } 

            if (position < -1.0) {                      // (-∞, -1) 
                page.setTranslationX(MAX_TRANSLATE); 
                page.setScaleX(MIN_SCALE); 
                page.setScaleY(MIN_SCALE); 
                page.setRotationY(-MAX_ROTATION); 
            } else if (firstPage || (!firstTime && position <= 1.0)) {      // [-1, 1] 
                firstPage = false; 

                page.setTranslationX(-MAX_TRANSLATE * position); 
                float scale = MIN_SCALE + (1-MIN_SCALE) * (1.0f - Math.abs(position)); 
                page.setScaleX(scale);      // 缩放 
                page.setScaleY(scale); 
                page.setRotationY(MAX_ROTATION * position);     // 旋转 

            } else if (firstTime || position > 1.0){        // (1, +∞) 

                page.setTranslationX(-MAX_TRANSLATE); 
                page.setScaleX(MIN_SCALE); 
                page.setScaleY(MIN_SCALE); 
                page.setRotationY(MAX_ROTATION); 
            } 
        } 
    }

形成的效果如下:


share3.gif

相关文章

网友评论

    本文标题:ViewPager 的使用

    本文链接:https://www.haomeiwen.com/subject/ngxcdctx.html