Android基础———ViewPager

ViewPager

可以用来做轮播,或者做fragment的滑动框架

一、简单的一个轮播

1.在布局文件layout里面写下viewpager控件
    `<android.support.v4.view.ViewPager
         android:id="@+id/viewpager"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
     </android.support.v4.view.ViewPager>`

注意:viewpager是v4包里面的

2.ViewPager也和listview这些控件类似,需要适配器来加载view,写一个适配器
    public class MyAdapter extends PagerAdapter {
        private List<ImageView> imageViewList;

        public MyAdapter(List<ImageView> imageViewList) {
            this.imageViewList = imageViewList;
        }

        /**
         * 获取viewpager的数目
         * @return
         */
        @Override
        public int getCount() {
            return imageViewList==null?0:imageViewList.size();
        }

        /**
         *判断 返回的view和填充到viewpager的view是否一致 一致的才返回true
         * @param view instantiateItem里面加入containerde的view
         * @param object instantiateItem里面返回的值
         */
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view==object;
        }


        /**
         * 加载item ,要显示的内容加载出来
         * @param container  viewpager
         * @param position   要加载的位置
         * @return
         */
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            //获取当前要加载的图片
            ImageView imageView=imageViewList.get(position);
            container.addView(imageView);
            return imageView;
        }

        /**
         * 销毁view
         * @param container  viewpager对象
         * @param position  当前位置
         * @param object    将要销毁的对象view
         */
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            //从viewpager中移除view
            container.removeView((View)object);
        }
    }

3.在activity里面加代码,和listview类似,获取数据源,传到适配器,设置适配器,下面代码,数据源为三张图片,已经加在资源文件中了,资源文件也可以是从网上获取
public class MainActivity extends AppCompatActivity {

    //图片id数组
    private int[] ids={R.mipmap.pic1,R.mipmap.pic2,R.mipmap.pic3,R.mipmap.pic4};
    private ViewPager viewPager;
    private List<ImageView> imageViewList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取viewpager控件
        viewPager = (ViewPager)findViewById(R.id.viewpager);
        //获取图片,把图片装入集合中
        imageViewList = new ArrayList<>();
        //给viewpager添加图片
        for(int i=0;i<ids.length;i++){
            //将图片加载到图片控件,然后加到图片集合中
            ImageView imageView=new ImageView(this);
            imageView.setImageResource(ids[i]);
            imageViewList.add(imageView);
        }
        //加载适配器
        viewPager.setAdapter(new MyAdapter(imageViewList));
    }
}

无限轮播

无限轮播需要注意的地方:会容易在移动view时崩溃,
    崩溃的原因,是从右往左滑的时候是先移除后加载,而往回滑的时候是先加载后移除。

解决崩溃主要思路:
        在适配器里面设置getcount时,将返回的值设置为Integer.MAX_VALUE;
             public int getCount() {
                return Integer.MAX_VALUE;
            }
        因为这个count代表的viewpager一共有多少页view,如果填实际的数据源,在循环无限的轮播的时候容易出问题,
        所以设置一个很大的数来,来充当数目。

        在加载view时要注意,判断当前获取的imageview是否有父容器,如果有,则需要移除父容器,没有则添加到viewpager,
        public Object instantiateItem(ViewGroup container, int position) {
                int rightPosition = position % imageViewList.size();//实际应该加入viewpager里面的第几个条目
                ImageView imageView = imageViewList.get(rightPosition);

                if (imageView.getParent() == null) {//如果获取到的imageView没有父容器就不添加到viewPager
                    container.addView(imageView);
                } else {//如果有父容器,就改变标记
                    isBack = true;
                }
                return imageView;
            }

        在销毁view时也要进行判断,判断是否为往左滑动,如果是,则不需要销毁view
             //销毁view
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                if (!isBack) {//不是往左边滑的,才移除
                    container.removeView((View) object);
                }
                isBack=false;
            }

    以上操作的原因:
        ViewPager在滑动时,每次加载时除了当前显示的视图,还会加载两个视图,前一个和后一个,
        第一次加载时,前一个没有,只加载后一个,也只有这一次是例外。
        但是每次加载前后两个图片的方式又根据滑动方式分为两种:
        1、当你向右边滑动时,加载顺序是:先销毁上一个,再去加载下一个的下一个
        2、当你向左边滑动时,加载顺序是:先加载上一个的上一个,在去销毁下一个

        因为以上两种加载方式,就会导致在只有3张图的时候,出现程序崩溃。
        比如有0 1 2 三张图,当前显示的是2,上一个是1和下一个0已经加载好了。
                          此时向左滑动,显示1,先加载0,会发现0已经有父容器了,加载失败,程序崩溃....

    在代码里面,当只有三张图的时候,可以再添加三张一样的图,这样就不会出现以上情况了、
    代码如下:(包括了handler实现自动轮播,以及小白点)
        public class Main2Activity extends AppCompatActivity {

        private ViewPager viewPager2;
        private List<ImageView> imageViewList;
        private int[] ids = {R.mipmap.pic1, R.mipmap.pic2, R.mipmap.pic3};
        private int lastPoint=0;//记录上一个小白点的位置

        //实现自动轮播
        private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 111://定时轮播
                        viewPager2.setCurrentItem(viewPager2.getCurrentItem() + 1);//设置轮播为下一页
                        sendEmptyMessageDelayed(111, 2000);  //延迟两秒轮播
                        break;
                }
            }
        };
        private LinearLayout pointLinearLayout;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main2);
            viewPager2 = (ViewPager) findViewById(R.id.viewpager2);
            imageViewList = new ArrayList<>();
            //用来放白点
            pointLinearLayout=(LinearLayout)findViewById(R.id.pointLinearLayout);

            //给list加imageview
            createImageview(false);
            //3张图以下,会因为viewpager不同方向滑动,加载方式不同而出现一些问题,所以此处可以判断一下,
            // 如果只有三张或一下的图,则多加三张一样的图
            if (imageViewList.size() <= 3) {
                createImageview(true);
            }
            viewPager2.setAdapter(new MyAdapterInfinite(imageViewList));
            //初始将viewpager设置在中间,这样才可以往左边滑动
            //Integer.MAX_VALUE/2-Integer.MAX_VALUE/2%imageViewList.size() 这个不仅取到了中间,而且取到了第一张图
            //Integer.MAX_VALUE/2%imageViewList.size() 表示 中间那个位置的图片是实际的第几张图片,然后减去数,就是第0张图片也就是第一张图片
            viewPager2.setCurrentItem(Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % imageViewList.size());
            handler.sendEmptyMessage(111);

            //viewpager的页面监听
            viewPager2.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

                /**
                 * 当页面滑动的时候
                 *
                 * @param position             当前正在滑动的那一页下标
                 * @param positionOffset       滑动的偏移量 百分比 已经滑动了多少了
                 * @param positionOffsetPixels 滑动的偏移量(像素),当前总共滑动了多少像素
                 */
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

                }

                /**
                 * 页面被选择时
                 * @param position 当前页面的位置
                 */
                @Override
                public void onPageSelected(int position) {
                    //用当前位置去求模得到小白点的位置,这里取余应该用ids,不能用imageviewlist,
                    //因为图片有可能会有复制的
                    int pointPosition=position%ids.length;
                    //获取小白点,并设置值
                    pointLinearLayout.getChildAt(pointPosition).setEnabled(false);
                    //改变上一个小白点的状体
                    pointLinearLayout.getChildAt(lastPoint).setEnabled(true);
                    //设置lastpoint的值为当前页面小白点
                    lastPoint=pointPosition;
                }

                /**
                 * 页面滑动的状态发生变化的时候
                 *
                 * @param state 包含拖动1 SCROLL_STATE_DRAGGING,
                 *              惯性滚动2 SCROLL_STATE_SETTLING,
                 *              停止0SCROLL_STATE_IDLE
                 */
                @Override
                public void onPageScrollStateChanged(int state) {

                }
            });

            //viewpager的触摸监听
            viewPager2.setOnTouchListener(new View.OnTouchListener() {
                /**
                 * 当触摸的时候停止轮播
                 * @param view 当前的view页面
                 * @param motionEvent 事件类型,按下,松开
                 * @return
                 */
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {

                    if(motionEvent.getAction()== MotionEvent.ACTION_DOWN){//按下
                        handler.removeMessages(111);//移除handler
                    }else if(motionEvent.getAction()==MotionEvent.ACTION_UP){
                        handler.sendEmptyMessageDelayed(111,2000);
                    }
                    return false;
                }
            });

        }

        //用来把图片加载到list
        //使用isCopy来判断是否是复制的数据,即图片数量小于等于3的时候
        private void createImageview(boolean isCopy) {
            for (int i = 0; i < ids.length; i++) {
                ImageView imageView = new ImageView(this);
                imageView.setImageResource(ids[i]);
                imageViewList.add(imageView);

                if (!isCopy) {//图片资源不是复制的,就生产小白点
                    View view = new View(this);
                    view.setBackgroundResource(R.drawable.selector);//设置背景色
                    if (i == 0) {//如果是第一个则设置为红色
                        view.setEnabled(false);
                    }

                    //将当前位置加入到tag,下次点击事件可以使用
                    view.setTag(i);

                    //加入小白点到线性布局
                    view.setOnClickListener(new View.OnClickListener() {
                        /**
                         * 小白点的点击监听
                         * @param view
                         */
                        @Override
                        public void onClick(View view) {
                            //获取点击的小白点的位置
                            int pointposition=(int)view.getTag();
                            //获取当前点击小白点位置和上个小白点的位置差
                            int sub=pointposition-lastPoint;
                            //viewpager当前页面去加上差值,就是点击了小白点后要显示的页面
                            viewPager2.setCurrentItem(viewPager2.getCurrentItem()+sub);
                        }
                    });
                    LinearLayout.LayoutParams layoutParams=new LinearLayout.LayoutParams(44,44);
                    layoutParams.rightMargin=10;
                    pointLinearLayout.addView(view,layoutParams);
                }
            }
        }
    }

    除了上面的一种方法:还可以在适配器里面,每次加载图片都new一个出来,而不是去源数据中获取已经存好了的imageview。
    代码如下:
         @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView imageView = new ImageView(container.getContext());
            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
            imageView.setImageResource(ids[position%ids.length]);
            container.addView(imageView);
            return imageView;
        }
    但是这样特别浪费内存.建议用上面的一种

TabLayout

一种可以与viewpager结合使用,很暴力的控件,需要添加一个依赖,因为是另外一个包里面的

1.在xml布局文件里面写
    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabIndicatorHeight="10dp"
        app:tabSelectedTextColor="#fff"
        app:tabMode="scrollable"
        android:background="@drawable/shape"
        app:tabIndicatorColor="#0f0"
        app:tabTextColor="#00f"
        ></android.support.design.widget.TabLayout>
    属性:
    tabIndicatorColor 下标指示器的颜色
    tabIndicatorHeight 下标指示器的高度
    tabTextColor tab 上面的文件的颜色
    tabSelectedTextColor 被选中的 tab的上面的字的颜色

2.在代码里面,viewpager的加载如上面一致,tablayout控件的设置,需要将获取到的TabLayout控件与viewpager联动,
     //将tablayout与viewpager设置联动
    tabLayout.setupWithViewPager(viewPager);
但是有一个问题,设置联动之后,如果给tablayout的tab设置了tag,与哪个 viewpager 保持联动, 这行代码会导致所有的 tab被删除然后重新添加,所以上面的属性都会被回收。所以想要tag生效,必须在最后再添加一次tag。

3.想要tablayout的文字显示出来,需要在viewpager的adapter里面重写一个方法
     @Override
    public CharSequence getPageTitle(int position) {
        return ss.get(position);
    }   
 这个方法会把文字添加进去。

viewpager结合fragment

1.只是适配器不同了,viewpager里面放fragment时,适配器不是pageadapter,而是用FragmentStatePagerAdapter和FragmentPageAdapter里面重写的方法也很简单,只有两个。

2.在代码里面的实现很简单,
    public class MainActivity extends AppCompatActivity {

    private ViewPager viewPager;
    private List<Fragment> fragmentList;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取viewpager
        viewPager = (ViewPager)findViewById(R.id.viewpager);
        //创建fragment
        fragmentList = new ArrayList<>();
        MyFragment myFragment = new MyFragment();
        MyFragment2 myFragment2 = new MyFragment2();
        MyFragment3 myFragment3 = new MyFragment3();
        MyFragment4 myFragment4 = new MyFragment4();

        //添加数据源
        fragmentList.add(myFragment);
        fragmentList.add(myFragment2);
        fragmentList.add(myFragment3);
        fragmentList.add(myFragment4);

        //给viewpager设置适配器
        /*
        FragmentManager fm=getSupportFragmentManager();
        viewPager.setAdapter(new MyAdapter(fm,fragmentList));
        */

        //另外一种适配器
        viewPager.setAdapter(new MyAdapterFragmentState(getSupportFragmentManager(),fragmentList));
        viewPager.setOffscreenPageLimit(2);//前后各加载2个
    }
}

3.viewpager 里面可以设置前后加载图片的数量,setOffscreenPageLimit
 viewPager.setOffscreenPageLimit(2);//前后各加载2个
好像最多可以设置为3,即前后加载3张,每次加载7张图
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值