android 自定义Viewpager实现无限循环

  1. 云栖社区>
  2. 博客>
  3. 正文

android 自定义Viewpager实现无限循环

code_xzh 2016-02-16 10:36:00 浏览2547
展开阅读全文

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

  Demo实现的效果图如下:

  

   

   Demo代码:

     工程目录如下图:

  

      废话不多说,上代码。

    1.主Activity代码如下

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.stevenhu.android.phone.ui;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;  
  7. import com.nostra13.universalimageloader.core.DisplayImageOptions;  
  8. import com.nostra13.universalimageloader.core.ImageLoader;  
  9. import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;  
  10. import com.nostra13.universalimageloader.core.assist.QueueProcessingType;  
  11. import com.stevenhu.android.phone.bean.ADInfo;  
  12. import com.stevenhu.android.phone.utils.ViewFactory;  
  13.   
  14. import android.annotation.SuppressLint;  
  15. import android.app.Activity;  
  16. import android.os.Bundle;  
  17. import android.view.View;  
  18. import android.widget.ImageView;  
  19. import android.widget.Toast;  
  20. import cn.androiddevelop.cycleviewpager.lib.CycleViewPager;  
  21. import cn.androiddevelop.cycleviewpager.lib.CycleViewPager.ImageCycleViewListener;  
  22. /** 
  23.  * 描述:主页 
  24.  * 
  25.  * @author stevenhu 
  26.  * @version 2015年5月8日 上午10:47:37 
  27.  */  
  28. public class MainActivity extends Activity {  
  29.   
  30.     private List<ImageView> views = new ArrayList<ImageView>();  
  31.     private List<ADInfo> infos = new ArrayList<ADInfo>();  
  32.     private CycleViewPager cycleViewPager;  
  33.       
  34.     private String[] imageUrls = {"http://img.taodiantong.cn/v55183/infoimg/2013-07/130720115322ky.jpg",  
  35.             "http://pic30.nipic.com/20130626/8174275_085522448172_2.jpg",  
  36.             "http://pic18.nipic.com/20111215/577405_080531548148_2.jpg",  
  37.             "http://pic15.nipic.com/20110722/2912365_092519919000_2.jpg",  
  38.             "http://pic.58pic.com/58pic/12/64/27/55U58PICrdX.jpg"};  
  39.       
  40.     @Override  
  41.     protected void onCreate(Bundle savedInstanceState) {  
  42.         super.onCreate(savedInstanceState);  
  43.         setContentView(R.layout.ui_main);  
  44.         configImageLoader();  
  45.         initialize();  
  46.     }  
  47.       
  48.     @SuppressLint("NewApi")  
  49.     private void initialize() {  
  50.           
  51.         cycleViewPager = (CycleViewPager) getFragmentManager()  
  52.                 .findFragmentById(R.id.fragment_cycle_viewpager_content);  
  53.           
  54.         for(int i = 0; i < imageUrls.length; i ++){  
  55.             ADInfo info = new ADInfo();  
  56.             info.setUrl(imageUrls[i]);  
  57.             info.setContent("图片-->" + i );  
  58.             infos.add(info);  
  59.         }  
  60.           
  61.         // 将最后一个ImageView添加进来  
  62.         views.add(ViewFactory.getImageView(this, infos.get(infos.size() - 1).getUrl()));  
  63.         for (int i = 0; i < infos.size(); i++) {  
  64.             views.add(ViewFactory.getImageView(this, infos.get(i).getUrl()));  
  65.         }  
  66.         // 将第一个ImageView添加进来  
  67.         views.add(ViewFactory.getImageView(this, infos.get(0).getUrl()));  
  68.           
  69.         // 设置循环,在调用setData方法前调用  
  70.         cycleViewPager.setCycle(true);  
  71.   
  72.         // 在加载数据前设置是否循环  
  73.         cycleViewPager.setData(views, infos, mAdCycleViewListener);  
  74.         //设置轮播  
  75.         cycleViewPager.setWheel(true);  
  76.   
  77.         // 设置轮播时间,默认5000ms  
  78.         cycleViewPager.setTime(2000);  
  79.         //设置圆点指示图标组居中显示,默认靠右  
  80.         cycleViewPager.setIndicatorCenter();  
  81.     }  
  82.       
  83.     private ImageCycleViewListener mAdCycleViewListener = new ImageCycleViewListener() {  
  84.   
  85.         @Override  
  86.         public void onImageClick(ADInfo info, int position, View imageView) {  
  87.             if (cycleViewPager.isCycle()) {  
  88.                 position = position - 1;  
  89.                 Toast.makeText(MainActivity.this,  
  90.                         "position-->" + info.getContent(), Toast.LENGTH_SHORT)  
  91.                         .show();  
  92.             }  
  93.               
  94.         }  
  95.   
  96.     };  
  97.       
  98.     /** 
  99.      * 配置ImageLoder 
  100.      */  
  101.     private void configImageLoader() {  
  102.         // 初始化ImageLoader  
  103.         @SuppressWarnings("deprecation")  
  104.         DisplayImageOptions options = new DisplayImageOptions.Builder().showStubImage(R.drawable.icon_stub) // 设置图片下载期间显示的图片  
  105.                 .showImageForEmptyUri(R.drawable.icon_empty) // 设置图片Uri为空或是错误的时候显示的图片  
  106.                 .showImageOnFail(R.drawable.icon_error) // 设置图片加载或解码过程中发生错误显示的图片  
  107.                 .cacheInMemory(true// 设置下载的图片是否缓存在内存中  
  108.                 .cacheOnDisc(true// 设置下载的图片是否缓存在SD卡中  
  109.                 // .displayer(new RoundedBitmapDisplayer(20)) // 设置成圆角图片  
  110.                 .build(); // 创建配置过得DisplayImageOption对象  
  111.   
  112.         ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()).defaultDisplayImageOptions(options)  
  113.                 .threadPriority(Thread.NORM_PRIORITY - 2).denyCacheImageMultipleSizesInMemory()  
  114.                 .discCacheFileNameGenerator(new Md5FileNameGenerator()).tasksProcessingOrder(QueueProcessingType.LIFO).build();  
  115.         ImageLoader.getInstance().init(config);       
  116.     }  
  117. }  

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

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.       
  7.   
  8.     <fragment  
  9.         android:id="@+id/fragment_cycle_viewpager_content"  
  10.         android:name="cn.androiddevelop.cycleviewpager.lib.CycleViewPager"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="180dip" />  
  13.        
  14.        
  15.     <RelativeLayout   
  16.         android:layout_width="fill_parent"  
  17.         android:layout_height="0dip"  
  18.         android:layout_weight="1">  
  19.           
  20.         <TextView   
  21.             android:layout_width="wrap_content"  
  22.             android:layout_height="wrap_content"  
  23.             android:layout_centerInParent="true"  
  24.             android:text="content"/>  
  25.     </RelativeLayout>  
  26.       
  27. </LinearLayout>  

   3.CycleViewPager类代码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package cn.androiddevelop.cycleviewpager.lib;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import android.annotation.SuppressLint;  
  7. import android.app.Fragment;  
  8. import android.os.Bundle;  
  9. import android.os.Message;  
  10. import android.support.v4.view.PagerAdapter;  
  11. import android.support.v4.view.ViewPager.OnPageChangeListener;  
  12. import android.view.LayoutInflater;  
  13. import android.view.View;  
  14. import android.view.View.OnClickListener;  
  15. import android.view.ViewGroup;  
  16. import android.widget.FrameLayout;  
  17. import android.widget.ImageView;  
  18. import android.widget.LinearLayout;  
  19. import android.widget.RelativeLayout;  
  20.   
  21. import com.stevenhu.android.phone.bean.ADInfo;  
  22. import com.stevenhu.android.phone.ui.R;  
  23.   
  24. /** 
  25.  * 实现可循环,可轮播的viewpager 
  26.  */  
  27. @SuppressLint("NewApi")  
  28. public class CycleViewPager extends Fragment implements OnPageChangeListener {  
  29.       
  30.     private List<ImageView> imageViews = new ArrayList<ImageView>();  
  31.     private ImageView[] indicators;  
  32.     private FrameLayout viewPagerFragmentLayout;  
  33.     private LinearLayout indicatorLayout; // 指示器  
  34.     private BaseViewPager viewPager;  
  35.     private BaseViewPager parentViewPager;  
  36.     private ViewPagerAdapter adapter;  
  37.     private CycleViewPagerHandler handler;  
  38.     private int time = 5000// 默认轮播时间  
  39.     private int currentPosition = 0// 轮播当前位置  
  40.     private boolean isScrolling = false// 滚动框是否滚动着  
  41.     private boolean isCycle = false// 是否循环  
  42.     private boolean isWheel = false// 是否轮播  
  43.     private long releaseTime = 0// 手指松开、页面不滚动时间,防止手机松开后短时间进行切换  
  44.     private int WHEEL = 100// 转动  
  45.     private int WHEEL_WAIT = 101// 等待  
  46.     private ImageCycleViewListener mImageCycleViewListener;  
  47.     private List<ADInfo> infos;  
  48.   
  49.     @Override  
  50.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  51.             Bundle savedInstanceState) {  
  52.         View view = LayoutInflater.from(getActivity()).inflate(  
  53.                 R.layout.view_cycle_viewpager_contet, null);  
  54.   
  55.         viewPager = (BaseViewPager) view.findViewById(R.id.viewPager);  
  56.         indicatorLayout = (LinearLayout) view  
  57.                 .findViewById(R.id.layout_viewpager_indicator);  
  58.   
  59.         viewPagerFragmentLayout = (FrameLayout) view  
  60.                 .findViewById(R.id.layout_viewager_content);  
  61.   
  62.         handler = new CycleViewPagerHandler(getActivity()) {  
  63.   
  64.             @Override  
  65.             public void handleMessage(Message msg) {  
  66.                 super.handleMessage(msg);  
  67.                 if (msg.what == WHEEL && imageViews.size() != 0) {  
  68.                     if (!isScrolling) {  
  69.                         int max = imageViews.size() + 1;  
  70.                         int position = (currentPosition + 1) % imageViews.size();  
  71.                         viewPager.setCurrentItem(position, true);  
  72.                         if (position == max) { // 最后一页时回到第一页  
  73.                             viewPager.setCurrentItem(1false);  
  74.                         }  
  75.                     }  
  76.   
  77.                     releaseTime = System.currentTimeMillis();  
  78.                     handler.removeCallbacks(runnable);  
  79.                     handler.postDelayed(runnable, time);  
  80.                     return;  
  81.                 }  
  82.                 if (msg.what == WHEEL_WAIT && imageViews.size() != 0) {  
  83.                     handler.removeCallbacks(runnable);  
  84.                     handler.postDelayed(runnable, time);  
  85.                 }  
  86.             }  
  87.         };  
  88.   
  89.         return view;  
  90.     }  
  91.   
  92.     public void setData(List<ImageView> views, List<ADInfo> list, ImageCycleViewListener listener) {  
  93.         setData(views, list, listener, 0);  
  94.     }  
  95.   
  96.     /** 
  97.      * 初始化viewpager 
  98.      *  
  99.      * @param views 
  100.      *            要显示的views 
  101.      * @param showPosition 
  102.      *            默认显示位置 
  103.      */  
  104.     public void setData(List<ImageView> views, List<ADInfo> list, ImageCycleViewListener listener, int showPosition) {  
  105.         mImageCycleViewListener = listener;  
  106.         infos = list;  
  107.         this.imageViews.clear();  
  108.   
  109.         if (views.size() == 0) {  
  110.             viewPagerFragmentLayout.setVisibility(View.GONE);  
  111.             return;  
  112.         }  
  113.   
  114.         for (ImageView item : views) {  
  115.             this.imageViews.add(item);  
  116.         }  
  117.   
  118.         int ivSize = views.size();  
  119.   
  120.         // 设置指示器  
  121.         indicators = new ImageView[ivSize];  
  122.         if (isCycle)  
  123.             indicators = new ImageView[ivSize - 2];  
  124.         indicatorLayout.removeAllViews();  
  125.         for (int i = 0; i < indicators.length; i++) {  
  126.             View view = LayoutInflater.from(getActivity()).inflate(  
  127.                     R.layout.view_cycle_viewpager_indicator, null);  
  128.             indicators[i] = (ImageView) view.findViewById(R.id.image_indicator);  
  129.             indicatorLayout.addView(view);  
  130.         }  
  131.   
  132.         adapter = new ViewPagerAdapter();  
  133.   
  134.         // 默认指向第一项,下方viewPager.setCurrentItem将触发重新计算指示器指向  
  135.         setIndicator(0);  
  136.   
  137.         viewPager.setOffscreenPageLimit(3);  
  138.         viewPager.setOnPageChangeListener(this);  
  139.         viewPager.setAdapter(adapter);  
  140.         if (showPosition < 0 || showPosition >= views.size())  
  141.             showPosition = 0;  
  142.         if (isCycle) {  
  143.             showPosition = showPosition + 1;  
  144.         }  
  145.         viewPager.setCurrentItem(showPosition);  
  146.   
  147.     }  
  148.   
  149.     /** 
  150.      * 设置指示器居中,默认指示器在右方 
  151.      */  
  152.     public void setIndicatorCenter() {  
  153.         RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(  
  154.                 RelativeLayout.LayoutParams.WRAP_CONTENT,  
  155.                 RelativeLayout.LayoutParams.WRAP_CONTENT);  
  156.         params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);  
  157.         params.addRule(RelativeLayout.CENTER_HORIZONTAL);  
  158.         indicatorLayout.setLayoutParams(params);  
  159.     }  
  160.   
  161.     /** 
  162.      * 是否循环,默认不开启,开启前,请将views的最前面与最后面各加入一个视图,用于循环 
  163.      *  
  164.      * @param isCycle 
  165.      *            是否循环 
  166.      */  
  167.     public void setCycle(boolean isCycle) {  
  168.         this.isCycle = isCycle;  
  169.     }  
  170.   
  171.     /** 
  172.      * 是否处于循环状态 
  173.      *  
  174.      * @return 
  175.      */  
  176.     public boolean isCycle() {  
  177.         return isCycle;  
  178.     }  
  179.   
  180.     /** 
  181.      * 设置是否轮播,默认不轮播,轮播一定是循环的 
  182.      *  
  183.      * @param isWheel 
  184.      */  
  185.     public void setWheel(boolean isWheel) {  
  186.         this.isWheel = isWheel;  
  187.         isCycle = true;  
  188.         if (isWheel) {  
  189.             handler.postDelayed(runnable, time);  
  190.         }  
  191.     }  
  192.   
  193.     /** 
  194.      * 是否处于轮播状态 
  195.      *  
  196.      * @return 
  197.      */  
  198.     public boolean isWheel() {  
  199.         return isWheel;  
  200.     }  
  201.   
  202.     final Runnable runnable = new Runnable() {  
  203.   
  204.         @Override  
  205.         public void run() {  
  206.             if (getActivity() != null && !getActivity().isFinishing()  
  207.                     && isWheel) {  
  208.                 long now = System.currentTimeMillis();  
  209.                 // 检测上一次滑动时间与本次之间是否有触击(手滑动)操作,有的话等待下次轮播  
  210.                 if (now - releaseTime > time - 500) {  
  211.                     handler.sendEmptyMessage(WHEEL);  
  212.                 } else {  
  213.                     handler.sendEmptyMessage(WHEEL_WAIT);  
  214.                 }  
  215.             }  
  216.         }  
  217.     };  
  218.   
  219.     /** 
  220.      * 释放指示器高度,可能由于之前指示器被限制了高度,此处释放 
  221.      */  
  222.     public void releaseHeight() {  
  223.         getView().getLayoutParams().height = RelativeLayout.LayoutParams.MATCH_PARENT;  
  224.         refreshData();  
  225.     }  
  226.   
  227.     /** 
  228.      * 设置轮播暂停时间,即没多少秒切换到下一张视图.默认5000ms 
  229.      *  
  230.      * @param time 
  231.      *            毫秒为单位 
  232.      */  
  233.     public void setTime(int time) {  
  234.         this.time = time;  
  235.     }  
  236.   
  237.     /** 
  238.      * 刷新数据,当外部视图更新后,通知刷新数据 
  239.      */  
  240.     public void refreshData() {  
  241.         if (adapter != null)  
  242.             adapter.notifyDataSetChanged();  
  243.     }  
  244.   
  245.     /** 
  246.      * 隐藏CycleViewPager 
  247.      */  
  248.     public void hide() {  
  249.         viewPagerFragmentLayout.setVisibility(View.GONE);  
  250.     }  
  251.   
  252.     /** 
  253.      * 返回内置的viewpager 
  254.      *  
  255.      * @return viewPager 
  256.      */  
  257.     public BaseViewPager getViewPager() {  
  258.         return viewPager;  
  259.     }  
  260.   
  261.     /** 
  262.      * 页面适配器 返回对应的view 
  263.      *  
  264.      * @author Yuedong Li 
  265.      *  
  266.      */  
  267.     private class ViewPagerAdapter extends PagerAdapter {  
  268.   
  269.         @Override  
  270.         public int getCount() {  
  271.             return imageViews.size();  
  272.         }  
  273.   
  274.         @Override  
  275.         public boolean isViewFromObject(View arg0, Object arg1) {  
  276.             return arg0 == arg1;  
  277.         }  
  278.   
  279.         @Override  
  280.         public void destroyItem(ViewGroup container, int position, Object object) {  
  281.             container.removeView((View) object);  
  282.         }  
  283.   
  284.         @Override  
  285.         public View instantiateItem(ViewGroup container, final int position) {  
  286.             ImageView v = imageViews.get(position);  
  287.             if (mImageCycleViewListener != null) {  
  288.                 v.setOnClickListener(new OnClickListener() {  
  289.                       
  290.                     @Override  
  291.                     public void onClick(View v) {  
  292.                         mImageCycleViewListener.onImageClick(infos.get(currentPosition - 1), currentPosition, v);  
  293.                     }  
  294.                 });  
  295.             }  
  296.             container.addView(v);  
  297.             return v;  
  298.         }  
  299.   
  300.         @Override  
  301.         public int getItemPosition(Object object) {  
  302.             return POSITION_NONE;  
  303.         }  
  304.     }  
  305.   
  306.     @Override  
  307.     public void onPageScrollStateChanged(int arg0) {  
  308.         if (arg0 == 1) { // viewPager在滚动  
  309.             isScrolling = true;  
  310.             return;  
  311.         } else if (arg0 == 0) { // viewPager滚动结束  
  312.             if (parentViewPager != null)  
  313.                 parentViewPager.setScrollable(true);  
  314.   
  315.             releaseTime = System.currentTimeMillis();  
  316.   
  317.             viewPager.setCurrentItem(currentPosition, false);  
  318.               
  319.         }  
  320.         isScrolling = false;  
  321.     }  
  322.   
  323.     @Override  
  324.     public void onPageScrolled(int arg0, float arg1, int arg2) {  
  325.     }  
  326.   
  327.     @Override  
  328.     public void onPageSelected(int arg0) {  
  329.         int max = imageViews.size() - 1;  
  330.         int position = arg0;  
  331.         currentPosition = arg0;  
  332.         if (isCycle) {  
  333.             if (arg0 == 0) {  
  334.                 currentPosition = max - 1;  
  335.             } else if (arg0 == max) {  
  336.                 currentPosition = 1;  
  337.             }  
  338.             position = currentPosition - 1;  
  339.         }  
  340.         setIndicator(position);  
  341.     }  
  342.   
  343.     /** 
  344.      * 设置viewpager是否可以滚动 
  345.      *  
  346.      * @param enable 
  347.      */  
  348.     public void setScrollable(boolean enable) {  
  349.         viewPager.setScrollable(enable);  
  350.     }  
  351.   
  352.     /** 
  353.      * 返回当前位置,循环时需要注意返回的position包含之前在views最前方与最后方加入的视图,即当前页面试图在views集合的位置 
  354.      *  
  355.      * @return 
  356.      */  
  357.     public int getCurrentPostion() {  
  358.         return currentPosition;  
  359.     }  
  360.   
  361.     /** 
  362.      * 设置指示器 
  363.      *  
  364.      * @param selectedPosition 
  365.      *            默认指示器位置 
  366.      */  
  367.     private void setIndicator(int selectedPosition) {  
  368.         for (int i = 0; i < indicators.length; i++) {  
  369.             indicators[i]  
  370.                     .setBackgroundResource(R.drawable.icon_point);  
  371.         }  
  372.         if (indicators.length > selectedPosition)  
  373.             indicators[selectedPosition]  
  374.                     .setBackgroundResource(R.drawable.icon_point_pre);  
  375.     }  
  376.   
  377.     /** 
  378.      * 如果当前页面嵌套在另一个viewPager中,为了在进行滚动时阻断父ViewPager滚动,可以 阻止父ViewPager滑动事件 
  379.      * 父ViewPager需要实现ParentViewPager中的setScrollable方法 
  380.      */  
  381.     public void disableParentViewPagerTouchEvent(BaseViewPager parentViewPager) {  
  382.         if (parentViewPager != null)  
  383.             parentViewPager.setScrollable(false);  
  384.     }  
  385.   
  386.       
  387.     /** 
  388.      * 轮播控件的监听事件 
  389.      *  
  390.      * @author minking 
  391.      */  
  392.     public static interface ImageCycleViewListener {  
  393.   
  394.         /** 
  395.          * 单击图片事件 
  396.          *  
  397.          * @param position 
  398.          * @param imageView 
  399.          */  
  400.         public void onImageClick(ADInfo info, int postion, View imageView);  
  401.     }  
  402. }  
    CycleViewPager类为实现可循环,可轮播的ViewPager的核心类,继承自Fragment,具体实现原理就不多说了,代码中都有相关的注释。

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

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

   http://download.csdn.net/detail/stevenhu_223/8675717


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

   http://download.csdn.net/detail/stevenhu_223/8697903

网友评论

登录后评论
0/500
评论