天天看点

android 自定义Viewpager实现无限循环

 前言:经常会看到有一些app的banner界面可以实现循环播放多个广告图片和手动滑动循环。本以为单纯的viewpager就可以实现这些功能。但是蛋疼的事情来了,viewpager并不支持循环翻页。所以要实现循环还得需要自己去动手。自己在网上也找了些例子,本博文的demo是结合自己找到的一些相关例子的基础上去改造,也希望对读者有用。

  demo实现的效果图如下:

android 自定义Viewpager实现无限循环

   demo代码:

     工程目录如下图:

android 自定义Viewpager实现无限循环

      废话不多说,上代码。

    1.主activity代码如下:

android 自定义Viewpager实现无限循环

package com.stevenhu.android.phone.ui;  

import java.util.arraylist;  

import java.util.list;  

import com.nostra13.universalimageloader.cache.disc.naming.md5filenamegenerator;  

import com.nostra13.universalimageloader.core.displayimageoptions;  

import com.nostra13.universalimageloader.core.imageloader;  

import com.nostra13.universalimageloader.core.imageloaderconfiguration;  

import com.nostra13.universalimageloader.core.assist.queueprocessingtype;  

import com.stevenhu.android.phone.bean.adinfo;  

import com.stevenhu.android.phone.utils.viewfactory;  

import android.annotation.suppresslint;  

import android.app.activity;  

import android.os.bundle;  

import android.view.view;  

import android.widget.imageview;  

import android.widget.toast;  

import cn.androiddevelop.cycleviewpager.lib.cycleviewpager;  

import cn.androiddevelop.cycleviewpager.lib.cycleviewpager.imagecycleviewlistener;  

/** 

 * 描述:主页 

 * 

 * @author stevenhu 

 * @version 2015年5月8日 上午10:47:37 

 */  

public class mainactivity extends activity {  

    private list<imageview> views = new arraylist<imageview>();  

    private list<adinfo> infos = new arraylist<adinfo>();  

    private cycleviewpager cycleviewpager;  

    private string[] imageurls = {"http://img.taodiantong.cn/v55183/infoimg/2013-07/130720115322ky.jpg",  

            "http://pic30.nipic.com/20130626/8174275_085522448172_2.jpg",  

            "http://pic18.nipic.com/20111215/577405_080531548148_2.jpg",  

            "http://pic15.nipic.com/20110722/2912365_092519919000_2.jpg",  

            "http://pic.58pic.com/58pic/12/64/27/55u58picrdx.jpg"};  

    @override  

    protected void oncreate(bundle savedinstancestate) {  

        super.oncreate(savedinstancestate);  

        setcontentview(r.layout.ui_main);  

        configimageloader();  

        initialize();  

    }  

    @suppresslint("newapi")  

    private void initialize() {  

        cycleviewpager = (cycleviewpager) getfragmentmanager()  

                .findfragmentbyid(r.id.fragment_cycle_viewpager_content);  

        for(int i = 0; i < imageurls.length; i ++){  

            adinfo info = new adinfo();  

            info.seturl(imageurls[i]);  

            info.setcontent("图片-->" + i );  

            infos.add(info);  

        }  

        // 将最后一个imageview添加进来  

        views.add(viewfactory.getimageview(this, infos.get(infos.size() - 1).geturl()));  

        for (int i = 0; i < infos.size(); i++) {  

            views.add(viewfactory.getimageview(this, infos.get(i).geturl()));  

        // 将第一个imageview添加进来  

        views.add(viewfactory.getimageview(this, infos.get(0).geturl()));  

        // 设置循环,在调用setdata方法前调用  

        cycleviewpager.setcycle(true);  

        // 在加载数据前设置是否循环  

        cycleviewpager.setdata(views, infos, madcycleviewlistener);  

        //设置轮播  

        cycleviewpager.setwheel(true);  

        // 设置轮播时间,默认5000ms  

        cycleviewpager.settime(2000);  

        //设置圆点指示图标组居中显示,默认靠右  

        cycleviewpager.setindicatorcenter();  

    private imagecycleviewlistener madcycleviewlistener = new imagecycleviewlistener() {  

        @override  

        public void onimageclick(adinfo info, int position, view imageview) {  

            if (cycleviewpager.iscycle()) {  

                position = position - 1;  

                toast.maketext(mainactivity.this,  

                        "position-->" + info.getcontent(), toast.length_short)  

                        .show();  

            }  

    };  

    /** 

     * 配置imageloder 

     */  

    private void configimageloader() {  

        // 初始化imageloader  

        @suppresswarnings("deprecation")  

        displayimageoptions options = new displayimageoptions.builder().showstubimage(r.drawable.icon_stub) // 设置图片下载期间显示的图片  

                .showimageforemptyuri(r.drawable.icon_empty) // 设置图片uri为空或是错误的时候显示的图片  

                .showimageonfail(r.drawable.icon_error) // 设置图片加载或解码过程中发生错误显示的图片  

                .cacheinmemory(true) // 设置下载的图片是否缓存在内存中  

                .cacheondisc(true) // 设置下载的图片是否缓存在sd卡中  

                // .displayer(new roundedbitmapdisplayer(20)) // 设置成圆角图片  

                .build(); // 创建配置过得displayimageoption对象  

        imageloaderconfiguration config = new imageloaderconfiguration.builder(getapplicationcontext()).defaultdisplayimageoptions(options)  

                .threadpriority(thread.norm_priority - 2).denycacheimagemultiplesizesinmemory()  

                .disccachefilenamegenerator(new md5filenamegenerator()).tasksprocessingorder(queueprocessingtype.lifo).build();  

        imageloader.getinstance().init(config);       

}  

   2.主文件ui_main.xml代码如下:

android 自定义Viewpager实现无限循环

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

    <fragment  

        android:id="@+id/fragment_cycle_viewpager_content"  

        android:name="cn.androiddevelop.cycleviewpager.lib.cycleviewpager"  

        android:layout_width="match_parent"  

        android:layout_height="180dip" />  

    <relativelayout   

        android:layout_width="fill_parent"  

        android:layout_height="0dip"  

        android:layout_weight="1">  

        <textview   

            android:layout_width="wrap_content"  

            android:layout_height="wrap_content"  

            android:layout_centerinparent="true"  

            android:text="content"/>  

    </relativelayout>  

</linearlayout>  

   3.cycleviewpager类代码如下:

android 自定义Viewpager实现无限循环

package cn.androiddevelop.cycleviewpager.lib;  

import android.app.fragment;  

import android.os.message;  

import android.support.v4.view.pageradapter;  

import android.support.v4.view.viewpager.onpagechangelistener;  

import android.view.layoutinflater;  

import android.view.view.onclicklistener;  

import android.view.viewgroup;  

import android.widget.framelayout;  

import android.widget.linearlayout;  

import android.widget.relativelayout;  

import com.stevenhu.android.phone.ui.r;  

 * 实现可循环,可轮播的viewpager 

@suppresslint("newapi")  

public class cycleviewpager extends fragment implements onpagechangelistener {  

    private list<imageview> imageviews = new arraylist<imageview>();  

    private imageview[] indicators;  

    private framelayout viewpagerfragmentlayout;  

    private linearlayout indicatorlayout; // 指示器  

    private baseviewpager viewpager;  

    private baseviewpager parentviewpager;  

    private viewpageradapter adapter;  

    private cycleviewpagerhandler handler;  

    private int time = 5000; // 默认轮播时间  

    private int currentposition = 0; // 轮播当前位置  

    private boolean isscrolling = false; // 滚动框是否滚动着  

    private boolean iscycle = false; // 是否循环  

    private boolean iswheel = false; // 是否轮播  

    private long releasetime = 0; // 手指松开、页面不滚动时间,防止手机松开后短时间进行切换  

    private int wheel = 100; // 转动  

    private int wheel_wait = 101; // 等待  

    private imagecycleviewlistener mimagecycleviewlistener;  

    private list<adinfo> infos;  

    public view oncreateview(layoutinflater inflater, viewgroup container,  

            bundle savedinstancestate) {  

        view view = layoutinflater.from(getactivity()).inflate(  

                r.layout.view_cycle_viewpager_contet, null);  

        viewpager = (baseviewpager) view.findviewbyid(r.id.viewpager);  

        indicatorlayout = (linearlayout) view  

                .findviewbyid(r.id.layout_viewpager_indicator);  

        viewpagerfragmentlayout = (framelayout) view  

                .findviewbyid(r.id.layout_viewager_content);  

        handler = new cycleviewpagerhandler(getactivity()) {  

            @override  

            public void handlemessage(message msg) {  

                super.handlemessage(msg);  

                if (msg.what == wheel && imageviews.size() != 0) {  

                    if (!isscrolling) {  

                        int max = imageviews.size() + 1;  

                        int position = (currentposition + 1) % imageviews.size();  

                        viewpager.setcurrentitem(position, true);  

                        if (position == max) { // 最后一页时回到第一页  

                            viewpager.setcurrentitem(1, false);  

                        }  

                    }  

                    releasetime = system.currenttimemillis();  

                    handler.removecallbacks(runnable);  

                    handler.postdelayed(runnable, time);  

                    return;  

                }  

                if (msg.what == wheel_wait && imageviews.size() != 0) {  

        };  

        return view;  

    public void setdata(list<imageview> views, list<adinfo> list, imagecycleviewlistener listener) {  

        setdata(views, list, listener, 0);  

     * 初始化viewpager 

     *  

     * @param views 

     *            要显示的views 

     * @param showposition 

     *            默认显示位置 

    public void setdata(list<imageview> views, list<adinfo> list, imagecycleviewlistener listener, int showposition) {  

        mimagecycleviewlistener = listener;  

        infos = list;  

        this.imageviews.clear();  

        if (views.size() == 0) {  

            viewpagerfragmentlayout.setvisibility(view.gone);  

            return;  

        for (imageview item : views) {  

            this.imageviews.add(item);  

        int ivsize = views.size();  

        // 设置指示器  

        indicators = new imageview[ivsize];  

        if (iscycle)  

            indicators = new imageview[ivsize - 2];  

        indicatorlayout.removeallviews();  

        for (int i = 0; i < indicators.length; i++) {  

            view view = layoutinflater.from(getactivity()).inflate(  

                    r.layout.view_cycle_viewpager_indicator, null);  

            indicators[i] = (imageview) view.findviewbyid(r.id.image_indicator);  

            indicatorlayout.addview(view);  

        adapter = new viewpageradapter();  

        // 默认指向第一项,下方viewpager.setcurrentitem将触发重新计算指示器指向  

        setindicator(0);  

        viewpager.setoffscreenpagelimit(3);  

        viewpager.setonpagechangelistener(this);  

        viewpager.setadapter(adapter);  

        if (showposition < 0 || showposition >= views.size())  

            showposition = 0;  

        if (iscycle) {  

            showposition = showposition + 1;  

        viewpager.setcurrentitem(showposition);  

     * 设置指示器居中,默认指示器在右方 

    public void setindicatorcenter() {  

        relativelayout.layoutparams params = new relativelayout.layoutparams(  

                relativelayout.layoutparams.wrap_content,  

                relativelayout.layoutparams.wrap_content);  

        params.addrule(relativelayout.align_parent_bottom);  

        params.addrule(relativelayout.center_horizontal);  

        indicatorlayout.setlayoutparams(params);  

     * 是否循环,默认不开启,开启前,请将views的最前面与最后面各加入一个视图,用于循环 

     * @param iscycle 

     *            是否循环 

    public void setcycle(boolean iscycle) {  

        this.iscycle = iscycle;  

     * 是否处于循环状态 

     * @return 

    public boolean iscycle() {  

        return iscycle;  

     * 设置是否轮播,默认不轮播,轮播一定是循环的 

     * @param iswheel 

    public void setwheel(boolean iswheel) {  

        this.iswheel = iswheel;  

        iscycle = true;  

        if (iswheel) {  

            handler.postdelayed(runnable, time);  

     * 是否处于轮播状态 

    public boolean iswheel() {  

        return iswheel;  

    final runnable runnable = new runnable() {  

        public void run() {  

            if (getactivity() != null && !getactivity().isfinishing()  

                    && iswheel) {  

                long now = system.currenttimemillis();  

                // 检测上一次滑动时间与本次之间是否有触击(手滑动)操作,有的话等待下次轮播  

                if (now - releasetime > time - 500) {  

                    handler.sendemptymessage(wheel);  

                } else {  

                    handler.sendemptymessage(wheel_wait);  

     * 释放指示器高度,可能由于之前指示器被限制了高度,此处释放 

    public void releaseheight() {  

        getview().getlayoutparams().height = relativelayout.layoutparams.match_parent;  

        refreshdata();  

     * 设置轮播暂停时间,即没多少秒切换到下一张视图.默认5000ms 

     * @param time 

     *            毫秒为单位 

    public void settime(int time) {  

        this.time = time;  

     * 刷新数据,当外部视图更新后,通知刷新数据 

    public void refreshdata() {  

        if (adapter != null)  

            adapter.notifydatasetchanged();  

     * 隐藏cycleviewpager 

    public void hide() {  

        viewpagerfragmentlayout.setvisibility(view.gone);  

     * 返回内置的viewpager 

     * @return viewpager 

    public baseviewpager getviewpager() {  

        return viewpager;  

     * 页面适配器 返回对应的view 

     * @author yuedong li 

    private class viewpageradapter extends pageradapter {  

        public int getcount() {  

            return imageviews.size();  

        public boolean isviewfromobject(view arg0, object arg1) {  

            return arg0 == arg1;  

        public void destroyitem(viewgroup container, int position, object object) {  

            container.removeview((view) object);  

        public view instantiateitem(viewgroup container, final int position) {  

            imageview v = imageviews.get(position);  

            if (mimagecycleviewlistener != null) {  

                v.setonclicklistener(new onclicklistener() {  

                    @override  

                    public void onclick(view v) {  

                        mimagecycleviewlistener.onimageclick(infos.get(currentposition - 1), currentposition, v);  

                });  

            container.addview(v);  

            return v;  

        public int getitemposition(object object) {  

            return position_none;  

    public void onpagescrollstatechanged(int arg0) {  

        if (arg0 == 1) { // viewpager在滚动  

            isscrolling = true;  

        } else if (arg0 == 0) { // viewpager滚动结束  

            if (parentviewpager != null)  

                parentviewpager.setscrollable(true);  

            releasetime = system.currenttimemillis();  

            viewpager.setcurrentitem(currentposition, false);  

        isscrolling = false;  

    public void onpagescrolled(int arg0, float arg1, int arg2) {  

    public void onpageselected(int arg0) {  

        int max = imageviews.size() - 1;  

        int position = arg0;  

        currentposition = arg0;  

            if (arg0 == 0) {  

                currentposition = max - 1;  

            } else if (arg0 == max) {  

                currentposition = 1;  

            position = currentposition - 1;  

        setindicator(position);  

     * 设置viewpager是否可以滚动 

     * @param enable 

    public void setscrollable(boolean enable) {  

        viewpager.setscrollable(enable);  

     * 返回当前位置,循环时需要注意返回的position包含之前在views最前方与最后方加入的视图,即当前页面试图在views集合的位置 

    public int getcurrentpostion() {  

        return currentposition;  

     * 设置指示器 

     * @param selectedposition 

     *            默认指示器位置 

    private void setindicator(int selectedposition) {  

            indicators[i]  

                    .setbackgroundresource(r.drawable.icon_point);  

        if (indicators.length > selectedposition)  

            indicators[selectedposition]  

                    .setbackgroundresource(r.drawable.icon_point_pre);  

     * 如果当前页面嵌套在另一个viewpager中,为了在进行滚动时阻断父viewpager滚动,可以 阻止父viewpager滑动事件 

     * 父viewpager需要实现parentviewpager中的setscrollable方法 

    public void disableparentviewpagertouchevent(baseviewpager parentviewpager) {  

        if (parentviewpager != null)  

            parentviewpager.setscrollable(false);  

     * 轮播控件的监听事件 

     * @author minking 

    public static interface imagecycleviewlistener {  

        /** 

         * 单击图片事件 

         *  

         * @param position 

         * @param imageview 

         */  

        public void onimageclick(adinfo info, int postion, view imageview);  

    cycleviewpager类为实现可循环,可轮播的viewpager的核心类,继承自fragment,具体实现原理就不多说了,代码中都有相关的注释。

  ok,接下来的其他类就不多说了。自己下载demo学习吧。

   本博文demo下载链接地址如下:

   另外,还有一种通过自定义viewpager实现和本博文相同效果的广告界面demo,这里就不再贴代码,可以通过如下地址下载:

继续阅读