很久没有写博客了,之前花时间写了一个Viewpager实现的无限图片轮播,个人感觉还是很好用的QAQ,源码和思路都还算清晰

实现的效果图如下:

android 图片循环滚动 安卓手机图片循环播放_Viewpager图片无限轮播

这里要补充一下,在这个项目中我把图片轮播写进了一个Viewholder里内嵌在了Recyclelistview里。但本文只介绍图片轮播部分的实现:

整体思路:使用handler的延时发送方法(sendEmptyMessageDelayed)实现在adapter中控制Viewpager图片轮播

具体实现分为三个部分:

首先是轮播图片的布局文件main_home_picturecarousel.xml:

<?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="130dp"
    android:orientation="vertical"
     >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="130dp">

        <android.support.v4.view.ViewPager
            android:id="@+id/main_picturecarousel_vp"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </android.support.v4.view.ViewPager>

        <LinearLayout
            android:layout_width="98dp"
            android:layout_height="7dp"
            android:orientation="horizontal"
            android:layout_marginBottom="10dp"
            android:layout_gravity="center_horizontal|bottom">
            <ImageView
                android:id="@+id/picturecarousel_img0"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian0"
                />
            <ImageView
                android:id="@+id/picturecarousel_img1"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img2"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img3"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img4"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img5"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img6"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
        </LinearLayout>
    </FrameLayout>
</LinearLayout>

Step1:ViewPager实现部分:

Viewpager需要绑定一个PagerAdapter,所以首先需要自定义一个PagerAdapter:

public class ImageAdapter extends PagerAdapter {
    private Context context;
    //轮播需要的图片
    public ArrayList<ImageView> imgs;

    public ImageAdapter(Context context, ArrayList<ImageView> imgs) {
        this.context = context;
        this.imgs = imgs;
    }

    /**
     * ViewPager的边界
     * @return
     */
    @Override
    public int getCount() {
        //设置成最大,使无限循环
        return 10000;
    }

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

    /**
     * 由于我们在instantiateItem()方法中已经处理了remove的逻辑,
     * 因此这里并不需要处理。实际上,实验表明这里如果加上了remove的调用,
     * 则会出现ViewPager的内容为空的情况。
     * @param container
     * @param position
     * @param object
     */
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        //警告:不要在这里调用removeView
    }

    /**
     * @param container
     * @param position
     * @return
     * 对position进行求模操作
     * 因为当用户向左滑时position可能出现负值,所以必须进行处理
     */
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        //对Viewpager页号求模去除View列表中要显示的项
        position %= imgs.size();
        if (position<0) {
            position = imgs.size() + position;
        }
        ImageView view = imgs.get(position);
        //如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。
    
        ViewParent viewParent = view.getParent();
        if (viewParent!=null){
            ViewGroup parent = (ViewGroup)viewParent;
            parent.removeView(view);
        }
        container.addView(view);


        return view;
    }
}

这里需要注意几个方面:1、最大值设置:可以设置为Interger.MAXVALUE实现无限(不过我觉得1W已经很多了。。)

2、instantiateItem方法中的页号显示逻辑处理部分,这里要考虑如果一开始位置设置为0往左滑会出现position为负值的情况

3、轮播之后View的移除


Step2:Handler实现计时轮播部分:

这里需要实现一个自定义的图片轮播用到的handler:

public class ImageCarouseHandler extends Handler {
    /**
     * 请求更新显示的View
     */
    public static final int MSG_UPDATE_IMAGE = 1;
    /**
     * 请求暂停轮播
     */
    public static final int MSG_KEEP_SILENT   = 2;
    /**
     * 请求恢复轮播。
     */
    public static final int MSG_BREAK_SILENT  = 3;
    /**
     * 记录最新的页号,当用户手动滑动时需要记录新页号,否则会使轮播的页面出错。
     * 例如当前如果在第一页,本来准备播放的是第二页,而这时候用户滑动到了末页,
     * 则应该播放的是第一页,如果继续按照原来的第二页播放,则逻辑上有问题。
     */
    public static final int MSG_PAGE_CHANGED  = 4;
    //轮播间隔时间
    public static final long MSG_DELAY = 3000;

    //这里使用弱引用避免Handler泄露
    private WeakReference<HomeFrag> weakReference;
    private int currentItem = Integer.MAX_VALUE/2;

    public ImageCarouseHandler(WeakReference<HomeFrag> wk) {
         weakReference = wk;
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        HomeFrag homeFrag = weakReference.get();
        if (homeFrag == null) {
            //HomeFrag已经回收,无需继续处理UI
            return;
        }
        //检查消息队列并移除未发送的消息,这主要是避免在复杂环境下消息出现重复等问题。
        /**
         * 这段会把第一次的自动轮播事件吃掉,所以可以加个条件,Position!=Max/2的时候才清除事件.因为第一次Position一定等于Max/2
         */

       if ((  homeFrag.handler.hasMessages(MSG_UPDATE_IMAGE))&&(currentItem!=Integer.MAX_VALUE/2)){
           homeFrag.handler.removeMessages(MSG_UPDATE_IMAGE);
       }
        switch (msg.what) {
            case MSG_UPDATE_IMAGE:
                currentItem++;
                homeFrag.ChanggeViewPagerCurrentItem(currentItem);
                //准备下次播放
                homeFrag.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
                break;
            case MSG_KEEP_SILENT:
                //只要不发送消息就暂停了
                break;
            case MSG_BREAK_SILENT:
                homeFrag.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
                break;
            case MSG_PAGE_CHANGED:
                //记录当前的页号,避免播放的时候页面显示不正确。
                currentItem = msg.arg1;
                break;
            default:
                break;
        }
    }
}



这里要注意设置不同状态的几种情况,以及要特别注意用户进行拖拽操作时要考虑到轮播暂停和拖拽事件结束后的恢复轮播的问题。

最后就是具体在内容中的代码实现部分了:

View view = LayoutInflater.from(context).inflate(R.layout.main_home_picturecarousel,null);
            vp = (ViewPager) view.findViewById(R.id.main_picturecarousel_vp);
            views = new ArrayList<>();
            LayoutInflater inflater = LayoutInflater.from(context);
            for (int i = 0;i<7; i++) {
                ImageView imgv = (ImageView) inflater.inflate(R.layout.picturecarouse_item,null);
                imgv.setImageResource(imgres[i]);
                views.add(imgv);
                //初始化点点
                pointimgs[i] = (ImageView) view.findViewById(pointimgsres[i]);
            }
            vp.setAdapter(new ImageAdapter(context,views));
vp.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
                showpoint(position);
                handler.sendMessage(Message.obtain(handler, ImageCarouseHandler.MSG_PAGE_CHANGED, position, 0));

            }

            //覆写该方法实现轮播效果
            @Override
            public void onPageScrollStateChanged(int state) {
                switch (state) {
                    case ViewPager.SCROLL_STATE_DRAGGING:
                        handler.sendEmptyMessage(ImageCarouseHandler.MSG_KEEP_SILENT);
                        break;
                    case ViewPager.SCROLL_STATE_IDLE:
                        handler.sendEmptyMessageDelayed(ImageCarouseHandler.MSG_UPDATE_IMAGE, ImageCarouseHandler.MSG_DELAY);
                        break;
                    default:
                        break;
                }
            }
        });
        vp.setCurrentItem(4998);
        //开始轮播效果
        handler.sendEmptyMessageDelayed(ImageCarouseHandler.MSG_UPDATE_IMAGE, ImageCarouseHandler.MSG_DELAY);
        //注意,设置Page 即缓存页面的个数,数过小时会出现fragment重复加载的问题

Step3:小圆点实现:

在布局文件中已经布置过小圆点的位置了,所以这里只要实现一个改变小圆点状态的方法:


<span style="font-family:宋体;">    private void showpoint(int position) {
        //dian0-白色 dian1-灰色
        for (int i = 0;i<7;i++) {
            pointimgs[i].setImageResource(R.drawable.dian1);
            pointimgs[position%7].setImageResource(R.drawable.dian0);
        }
    }</span>


然后在viewpager的onPageSelected()方法中调用:


<span style="font-family:宋体;">            @Override
            public void onPageSelected(int position) {
                showpoint(position);
                handler.sendMessage(Message.obtain(handler, ImageCarouseHandler.MSG_PAGE_CHANGED, position, 0));

            }</span>