• ViewPage最全解析


    简单说明:

      ViewPager是android扩展包v4包中的类,直接继承了ViewGroup类,和LinearLayout等布局一样,都是一个容器,需要在里面添加我们想要显示的内容。

    一、在xml中添加ViewPager  

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@id/bannerContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false">
    
        <android.support.v4.view.ViewPager
            android:id="@id/bannerViewPager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipChildren="false" />

    注:除了通过xml代码来定义一个ViewPager外,同其他控件一样也可以动态添加。

      

    二、定义ViewPageAdapter

      1、可以在构造函数中传递ViewPager需要加载显示的数据。

      2、必须重载的函数:

        a)  Determines whether a page View is associated with a specific key object return by  instantiateItem(ViewGroup ,int)

     public abstract boolean isViewFromObject(View view, Object object);

         b)   获取Pager的总页数

     /**
         * Return the number of views available.
         */
        public abstract int getCount();

       c)干函数中需要将  页面需要加载的内容添加到container中,同时将需要加载的view返回。该函数是PagerAdapter的核心,具体可以参照下面代码

    /**
         * Create the page for the given position.  The adapter is responsible
         * for adding the view to the container given here, although it only
         * must ensure this is done by the time it returns from
         * {@link #finishUpdate(ViewGroup)}.
         *
         * @param container The containing View in which the page will be shown.
         * @param position The page position to be instantiated.
         * @return Returns an Object representing the new page.  This does not
         * need to be a View, but can be some other container of the page.
         */
        public Object instantiateItem(ViewGroup container, int position) {
            return instantiateItem((View) container, position);
        }

      d)该函数负责对ViewGroup中已经添加的部分资源进行回收

        (遇到过当没有重载该函数时,滑动到图片页数第三张时,activity自动结束返回到上一个Activity。具体原因有待分析)

     /**
         * Remove a page for the given position.  The adapter is responsible
         * for removing the view from its container, although it only must ensure
         * this is done by the time it returns from {@link #finishUpdate(ViewGroup)}.
         *
         * @param container The containing View from which the page will be removed.
         * @param position The page position to be removed.
         * @param object The same object that was returned by
         * {@link #instantiateItem(View, int)}.
         */
        public void destroyItem(ViewGroup container, int position, Object object) {
            destroyItem((View) container, position, object);
        }

       e) destoryItem() 和  instantiateItem()  执行顺序问题:下图为 移动一个viewpage时 这两个函数打印的 position

                                 

      ViewPager启动时,第一个和第二个页面已经加载完成。

      当再次滑动时,先加载第三个页面,然后再销毁第一个页面。(如果这时返回第1个页面,则先加载第一个页面再销毁第三个页面)

      总之,除了第一个后最后一个页面外,ViewPager总会保持左右两侧的页面已经加载完成。

    3、 PagerAdapter 还有 FragementPagerAdapter 用于加载Fragment。 (用普通的PagerAdapter加载Fragment的区别)

      FragementStatePagerAdapter : 当页数较多时还可以使用该Adapter。 

    PagerAdapter的实现代码: 

    /**
         *  PagerAdapter的实现
         * */
        public class DripPageGuideAdapter extends PagerAdapter {
    
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                container.addView(mViews.get(position));
                return mViews.get(position);
            }
    
            @Override
            public int getCount() {
                return totalSize;
            }
    
            @Override
            public boolean isViewFromObject(View view, Object object) {
                return view == object;
            }
    
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView((View)object);
            }
        }
    View Code

    三、 activity中定义viewpage,始化相关控件,并绑定二中定义的adapter

     1、加载ViewPager,绑定Adapter

    public class ViewPageActivity extends Activity {
    
        private ViewPager viewPager;
        private ViewPageAdapter adapter;
        private Set<String> citiesSHaredPreference;
    
        private Context context = ViewPageActivity.this;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_viewpage);
    
            citiesSHaredPreference = new HashSet<String>();
            citiesSHaredPreference.add("huoshan");
            citiesSHaredPreference.add("hefei");
            citiesSHaredPreference.add("shanghai");
    
            initLoadData();
            initView();
        }
    
        private void initLoadData(){
            for(int i=0;i<citiesSHaredPreference.size();i++){
    
            }
        }
    
        private void initView() {
            viewPager = (ViewPager) findViewById(R.id.viewpage);
            adapter = new ViewPageAdapter(context,citiesSHaredPreference);
            viewPager.setAdapter(adapter);
        }
    }

      当在adapter中添加图片时,如果要想图片适应ViewPager大小,需要设置:  imageView.setScaleType(ImageView.ScaleType.FIT_XY); 

     2、可以在布局中添加页面指示器: 圆圈/数字/横线,并且当滑动到某一页面时,指示器颜色出现变化。

    void createIndicator() {
            List<ImageView> indicatorImages = new ArrayList<>();
    
            for (int i = 0; i < 2; i++) {
                ImageView imageView = new ImageView(mContext);
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(90, 90);
                params.leftMargin = 5;
                params.rightMargin = 5;
    
                if (i == 0) {
                    //imageView.setImageResource(R.drawable.gray_radius);
                    imageView.setImageDrawable(IndicatorShapes.getGrayShape());
                } else {
                    imageView.setImageDrawable(IndicatorShapes.getWhiteShape());
                }
    
                indicatorImages.add(imageView);
                mIndicator.addView(imageView, params);
            }
        }

     

    仅仅初始化指示器还不够,还需要在滑动页面的时候指示器的颜色也跟随着改变,要达到此效果需设置viewpager的监听器:

      定一个lastPosition变量,初始值为0,当页面滑动时,需要把上一页面的指示器颜色恢复,同时设置当前页面为选中状态。

    mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                
               }
    
                @Override
                public void onPageSelected(int position) {
                    if(GuideConfig.isShowIndicator){
                        indicatorImages.get((lastPosition + totalSize) % totalSize).setImageDrawable(IndicatorShapes.getUnselectedShape());
                        indicatorImages.get((position + totalSize) % totalSize).setImageDrawable(IndicatorShapes.getSelectedShape());
                    }
                    lastPosition = position;
                    mListener.onPageSelected(totalSize,position);
                }
    
                @Override
                public void onPageScrollStateChanged(int state) {
               
                    }
                }
            });

    指示器开源库推荐:

      a、 安卓大神的 :  https://github.com/JakeWharton/ViewPagerIndicator

          该库长时间没有维护,因为是大神的所以放在第一个

      b、一个比我还年轻的人写的,自惭形秽:https://github.com/hackware1993/MagicIndicator

    3、滑动到最后页面再滑动监听事件: 很多业务需要当再滑动最后一个页面时需要跳转到下一个Activity的需求

      那么如何实现呢,其实就是在上面的监听接口中就能实现,可参考:http://blog.csdn.net/nn955/article/details/46240371

    4、滑动到最后页面继续向左拖动时,会有 半透明的条出现,如何禁止该条的出现

    四、ViewPager setOnPageChangeListener方法

    OnPageChangeListener接口中定义了三个函数: 

    1.onPageSelected(int position):这个方法有一个参数position,代表哪个页面被选中。当用手指滑动翻页的时候,如果翻动成功了(滑动的距离够长),手指抬起来就会立即执行这个方法,position就是当前滑动到的页面。如果直接setCurrentItem翻页,那position就和setCurrentItem的参数一致,这种情况在onPageScrolled执行方法前就会立即执行。

    2.onPageScrolled(int position,float positionOffset, int positionOffsetPixels):这个方法会在屏幕滚动过程中不断被调用。
    有三个参数,第一个position,这个参数要特别注意一下。当用手指滑动时,如果手指按在页面上不动,position和当前页面index是一致的;如果手指向左拖动(相应页面向右翻动),这时候position大部分时间和当前页面是一致的,只有翻页成功的情况下最后一次调用才会变为目标页面;如果手指向右拖动(相应页面向左翻动),这时候position大部分时间和目标页面是一致的,只有翻页不成功的情况下最后一次调用才会变为原页面。
    当直接设置setCurrentItem翻页时,如果是相邻的情况(比如现在是第二个页面,跳到第一或者第三个页面),如果页面向右翻动,大部分时间是和当前页面是一致的,只有最后才变成目标页面;如果向左翻动,position和目标页面是一致的。这和用手指拖动页面翻动是基本一致的。
    如果不是相邻的情况,比如我从第一个页面跳到第三个页面,position先是0,然后逐步变成1,然后逐步变成2;我从第三个页面跳到第一个页面,position先是1,然后逐步变成0,并没有出现为2的情况。
    positionOffset是当前页面滑动比例,如果页面向右翻动,这个值不断变大,最后在趋近1的情况后突变为0。如果页面向左翻动,这个值不断变小,最后变为0。
    positionOffsetPixels是当前页面滑动像素,变化情况和positionOffset一致。


    3.onPageScrollStateChanged(int state):这个方法在手指操作屏幕的时候发生变化。有三个值:0(END),1(PRESS) , 2(UP) 。
    当用手指滑动翻页时,手指按下去的时候会触发这个方法,state值为1,手指抬起时,如果发生了滑动(即使很小),这个值会变为2,然后最后变为0 。总共执行这个方法三次。一种特殊情况是手指按下去以后一点滑动也没有发生,这个时候只会调用这个方法两次,state值分别是1,0 。

    当setCurrentItem翻页时,会执行这个方法两次,state值分别为2 , 0 。


    三个方法的执行顺序为:用手指拖动翻页时,最先执行一遍onPageScrollStateChanged(1),然后不断执行onPageScrolled,放手指的时候,直接立即执行一次onPageScrollStateChanged(2),然后立即执行一次onPageSelected,然后再不断执行onPageScrollStateChanged,最后执行一次onPageScrollStateChanged(0)。

    易忽视问题:

      OnPageSelected 方法会有一个问题,当ViewPager第一个页面显示(position 为0)的时候该方法不会被调用。解决办法:

    将OnPageSelected 定义成一个私有成员变量,在调用mViewPager.setOnPageChangeListener(mOnPagerChageListener)之后马上调用

    mOnPageChangeListener.OnPageSelected(0);

    五、如何给页面滑动添加自定义的动画

    六、OOM问题

      当ViewPager滑动图片较多或者图片较大时容易出现OOM,那么如何优化图片加载呢:

    七、关于ViewPager相关的开源库推荐

     1、广告轮播,可循环自动播放:     https://github.com/youth5201314/banner     

       2、基于ViewPage的引导页面例子: https://github.com/matrixxun/ProductTour

      上面两个只是很实用的库,github上很多炫酷的viewPager效果,有时间可以研究下。

      

  • 相关阅读:
    Python
    Python
    Python
    Python
    Python
    Python
    Python
    python
    对象
    py常用模块
  • 原文地址:https://www.cnblogs.com/NeilZhang/p/6891231.html
Copyright © 2020-2023  润新知