天天看點

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,這裡就不再貼代碼,可以通過如下位址下載下傳:

繼續閱讀