Android 自动无限轮播的轮播图(附源码)(通过InfiniteShufflingViewPager解决冲突)

          轮播图是 绝大多数的项目都要实现的功能,因为比较常用且并不复杂,所以出现了各种各样的轮播图,今天就做一个可以自动无限轮播的轮播图。废话不多说,直接进入正题。
        注:文章末尾附项目源码下载链接。

        效果展示

        主要功能包括:通过Handler实现自动无限轮播、小圆点背景选择器、InfiniteShufflingViewPager解决冲突、轮播图数量为1时禁止滑动、Adapter的特殊处理等。下面先看下效果:

        

        页面布局

        以下为轮播图的页面布局文件代码:该文件中使用了自定义控件InfiniteShufflingViewPager,该控件的功能包括:1.请求父控件及祖宗控件不要拦截事件;2.解决ScrollView和ViewPager的上下滑动冲突;3.当ViewPager填充的数据为1的时候,让其不能滑动;
        注:文章末尾附InfiniteShufflingViewPager源码。  
<?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:background="#333333"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="180dp">

        <com.wangyang.infiniteshufflingviewpager.customview.InfiniteShufflingViewPager
            android:id="@+id/vp"
            android:layout_width="match_parent"
            android:layout_height="180dp" />

        <LinearLayout
            android:id="@+id/ll_point_container"
            android:layout_width="match_parent"
            android:layout_height="20dp"
            android:layout_alignParentBottom="true"
            android:gravity="center"
            android:orientation="horizontal"
            android:paddingLeft="5dp"
            android:paddingRight="5dp" />
    </RelativeLayout>
</LinearLayout>

        初始化PagerAdapter

    为ViewPager初始化数据适配器PagerAdapter,用法基本上保持一致,特殊处理两点:1.将数量设置为Integer.MAX_VALUE(Integer的最大值);2.重新计算position(int newPosition = position % img.length);以下为PagerAdapter的代码:
/**
 * 无限轮播的ViewPager的adapter
 * 1.将数量设置为Integer.MAX_VALUE;
 * 2.重新计算position(int newPosition = position % img.length;)
 */
PagerAdapter infiniteShufflingViewPagerAdapter = new PagerAdapter() {

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        //因为要无限轮循播放
        final int newPosition = position % img.length;

        ImageView imageView = new ImageView(context);
        imageView.setImageResource(img[newPosition]);
        imageView.setScaleType(ImageView.ScaleType.FIT_XY);
        container.addView(imageView);
        return imageView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }
};

        初始化小圆点

        根据img的数量初始化小圆点,1.创建View;2.设置背景选择器;3.设置宽高;4.设置左外间距(除了第一个);5.添加到容器中;6.设置都不可用(默认是可用的,即亮的);以下为初始化小圆点的代码:
/**
 * 初始化小圆点
 */
private void initPoint() {
    if (img != null && img.length > 0) {
        for (int i = 0; i < img.length; i++) {
            //创建指示器(小白点)
            View pointView = new View(context);
            //设置背景选择器
            pointView.setBackgroundResource(R.drawable.selector_bg_point);
            //设置宽高
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(DensityUtil.dip2px(5, context), DensityUtil.dip2px(5, context));
            //设置左外间距(除了第一个)
            if (i != 0) {
                params.leftMargin = DensityUtil.dip2px(10, context);
            }

            llPointContainer.addView(pointView, params);
            //设置都不可用(默认是可用的,即亮的)
            pointView.setEnabled(false);
        }
    }
}

        以下为背景选择器的文件代码:

        shape_bg_point_disable.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#ffffff" />
    <stroke
        android:width="1dp"
        android:color="#f47a39" />
</shape>

        shape_bg_point_enable.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#f47a39" />
</shape>
        selector_bg_point.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_enabled="true" android:drawable="@drawable/shape_bg_point_enable"></item>
    <item android:state_enabled="false"    android:drawable="@drawable/shape_bg_point_disable"></item>
</selector>

        初始化ViewPager

        最后对ViewPager进行初始化操作,实现通过Handler实现自动无限轮播、小圆点的展示、轮播图数量为1时禁止滑动等操作。以下为初始化ViewPager的代码:
/**
 * 初始化ViewPager
 */
private void initViewPager() {
    //异常处理
    if (img == null || img.length <= 0) {
        return;
    }

    //设置数据适配器
    vp.setAdapter(infiniteShufflingViewPagerAdapter);
    if (img.length > 1) {
        if (llPointContainer.getChildCount() > 0) {
            //设置到某个位置,使左右都可无限滑动
            vp.setCurrentItem(50000 + llPointContainer.getChildCount() - 50000 % llPointContainer.getChildCount());
            //设置第一个圆点默认是可用的
            llPointContainer.getChildAt(0).setEnabled(true);
            previousSelectedPosition = 0;
        }

        vp.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                int newPosition = position % img.length;

                //指示器:将之前的禁用,把最新的启用,更新指示器
                llPointContainer.getChildAt(newPosition).setEnabled(true);
                llPointContainer.getChildAt(previousSelectedPosition).setEnabled(false);

                //记录之前的位置和时间
                previousSelectedPosition = newPosition;
                startTime = System.currentTimeMillis();
            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

        //通过Handler使ViewPager无限轮播
        if (mHandler == null) {
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    endTime = System.currentTimeMillis();

                    if (endTime - startTime >= 2000) {
                        vp.setCurrentItem(vp.getCurrentItem() + 1);
                        //继续发送延时3秒的消息,形成内循环
                        mHandler.sendEmptyMessageDelayed(0, 3000);
                    }
                }
            };

            new Thread() {
                @Override
                public void run() {
                    //启动自动轮播逻辑(保证只执行一次)
                    mHandler.sendEmptyMessageDelayed(0, 3000);
                }
            }.start();
        }

        //当手指按住viewpager时,停止自动轮播;
        //当手指松开viewpager时,开启自动轮播;
        vp.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        //停止广告自动轮播,删除handler的所有消息
                        mHandler.removeCallbacksAndMessages(null);
                        break;
                    case MotionEvent.ACTION_CANCEL:
                        //启动广告
                        mHandler.sendEmptyMessageDelayed(0, 3000);
                        break;
                    case MotionEvent.ACTION_UP:
                        //启动广告
                        mHandler.sendEmptyMessageDelayed(0, 3000);
                        break;
                }
                return false;
            }
        });
    } else {
        //当轮播图数量为1时,禁止ViewPager滑动
        vp.setCanScroll(false);
    }
}

        小结

        以上就是轮播图的实现过程了,其它具体的实现细节,文章末尾的源码中都有详细的注解,在这里就不一一赘述了。实际开发中还是需要通过接口获取轮播图的图片,不管以哪种形式获取图片,都要注意图片的大小,防止出现OOM(Out of Memory)内存溢出异常,导致程序崩溃。


        附:

        GitHub项目地址
        DensityUtil源码

猜你喜欢

转载自blog.csdn.net/qq941263013/article/details/80988321