ViewPager实现轮播广告图

简介:

轮播广告在现在的应用中比较常见,下面就来实现下该功能(文章参考了网上流传的黑马的视频教程)先来看下具体的实现效果: 

实现思路:

1.为ViewPager设置数据源,实现ViewPager的滚动

2.将圆点指示器与ViewPager的页面对应起来

3.实现左右滑动均能无限循环

4.实现自动播放

5.实现当手指滑动的时候取消自动播发

首先需要说明的就是在这篇文章里并没有同时实现左右循环滑动和手指触碰自动停止的功能,这个问题还没有解决,留待以后再解决,也希望有知道的朋友在下面留言,先谢谢拉。

1.首先我们定义布局文件,用来显示Banner

这里我们采用对的是相对布局RelativeLayout,当然也可以采用帧布局FrameLayout。布局文件activity_mian.xml代码:


  
  
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     xmlns:tools="http://schemas.android.com/tools" 
  4.     android:id="@+id/activity_main" 
  5.     android:layout_width="match_parent" 
  6.     android:layout_height="match_parent" 
  7.     tools:context="com.qc.admin.mylunbotu.MainActivity"
  8.  
  9.     <RelativeLayout 
  10.         android:layout_width="match_parent" 
  11.         android:layout_height="160dp"
  12.  
  13.         <android.support.v4.view.ViewPager 
  14.             android:id="@+id/viewpager" 
  15.             android:layout_width="match_parent" 
  16.             android:layout_height="match_parent"
  17.  
  18.         </android.support.v4.view.ViewPager> 
  19.  
  20.         <LinearLayout 
  21.             android:layout_width="match_parent" 
  22.             android:layout_height="40dp" 
  23.             android:layout_alignParentBottom="true" 
  24.             android:background="#66000000" 
  25.             android:gravity="center_horizontal" 
  26.             android:orientation="vertical" 
  27.             android:padding="5dp"
  28.  
  29.             <TextView 
  30.                 android:id="@+id/tv_desc" 
  31.                 android:layout_width="wrap_content" 
  32.                 android:layout_height="wrap_content" 
  33.                 android:maxLines="1" 
  34.                 android:text="这是图片描述" 
  35.                 android:textColor="@android:color/white" /> 
  36.  
  37.             <!--用来填充圆点指示器的容器--> 
  38.             <LinearLayout 
  39.                 android:id="@+id/point_container" 
  40.                 android:layout_width="wrap_content" 
  41.                 android:layout_height="wrap_content" 
  42.                 android:layout_marginTop="5dp" 
  43.                 android:orientation="horizontal" /> 
  44.         </LinearLayout> 
  45.     </RelativeLayout> 
  46. </RelativeLayout>  

2.初始化圆点指示器

这里我们提供的图片数据和显示的文本数据都是静态的,直接从数组资源中获得的,如:


  
  
  1. //图片资源id数组 
  2.         imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e}; 
  3.         // 文本描述 
  4.         contentDescs = new String[]{ 
  5.                 "巩俐不低俗,我就不能低俗"
  6.                 "扑树又回来啦!再唱经典老歌引万人大合唱"
  7.                 "揭秘北京电影如何升级"
  8.                 "乐视网TV版大派送"
  9.                 "热血屌丝的反杀" 
  10.         };  

我们可以自己定义两个shape,来显示圆点指示器的两种不同显示状态,然后再定义一个selector,将它设置为控件的背景(ImageView),通过其setEnable(Boolean bool)方法可以自动的决定使用哪个图片。代码分别如下:

a.正常状态下,灰色圆点,point_normal.xml:


  
  
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:shape="oval"
  4.     <size 
  5.         android:width="5dp" 
  6.         android:height="5dp" /> 
  7.     <solid android:color="#44000000" /> 
  8.  
  9. </shape>  

b.点击状态下,白色圆点 point_pressed.xml:


  
  
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:shape="oval"
  4.     <size android:width="5dp" android:height="5dp"/> 
  5.     <solid android:color="#FFFFFFFF"/> 
  6. </shape>  

c.设置背景时的xml文件, point_bg.xml:


  
  
  1. <selector xmlns:android="http://schemas.android.com/apk/res/android"
  2.  
  3.     <item android:state_enabled="true" android:drawable="@drawable/point_pressed"/> 
  4.     <item android:state_enabled="false" android:drawable="@drawable/point_normal"/> 
  5. </selector>  

具体代码:


  
  
  1. private void initData() { 
  2.  
  3.        //图片资源id数组 
  4.        imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e}; 
  5.        // 文本描述 
  6.        contentDescs = new String[]{ 
  7.                "巩俐不低俗,我就不能低俗"
  8.                "扑树又回来啦!再唱经典老歌引万人大合唱"
  9.                "揭秘北京电影如何升级"
  10.                "乐视网TV版大派送"
  11.                "热血屌丝的反杀" 
  12.        }; 
  13.        imageViewList = new ArrayList<>(); 
  14.        LinearLayout.LayoutParams mParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); 
  15.        mParams.leftMargin = 15; 
  16.        mParams.topMargin = 2; 
  17.  
  18.        ImageView imageView; 
  19.        ImageView pointView; 
  20.        //初始化要展示的ImageView,并添加圆点指示器 
  21.        for (int i = 0; i < imageResIds.length; i++) { 
  22.            // 初始化图片 
  23.            imageView = new ImageView(this); 
  24.            imageView.setBackgroundResource(imageResIds[i]); 
  25.            imageViewList.add(imageView); 
  26.  
  27.            // 初始化指示器 
  28.            pointView = new ImageView(this); 
  29.            pointView.setBackgroundResource(R.drawable.point_bg); 
  30.            pointView.setLayoutParams(mParams); 
  31.            if (i == 0) { 
  32.                textView.setText(contentDescs[0]); 
  33.                pointView.setEnabled(true); 
  34.            } else { 
  35.                pointView.setEnabled(false); 
  36.            } 
  37.            mPointsLayout.addView(pointView); 
  38.        } 
  39.    }  

注意:创建shape文件的时候,如果找不到,可以切换到project目录下,在drawable目录下右键,new->Drawable resource file ,得到如下界面:

将selector直接改为shape即可

3.为ViewPager设置适配器


  
  
  1. class MyAdapter extends PagerAdapter { 
  2.  
  3.        // 1.返回条目的总数 
  4.        @Override 
  5.        public int getCount() { 
  6.  
  7.            //return imageViewList.size(); 
  8.            return Integer.MAX_VALUE; 
  9.        } 
  10.  
  11.        // 2.返回要显示的条目,并创建条目 
  12.        @Override 
  13.        public Object instantiateItem(ViewGroup container, int position) { 
  14.            //container:容器,其实也就是ViewPager 
  15.            //position:当前要显示的条目的位置 
  16.  
  17.            int newPosition = position % imageViewList.size(); 
  18.            ImageView imageView = imageViewList.get(newPosition); 
  19.            //a.将View对象添加到container容器中 
  20.            container.addView(imageView); 
  21.            //b.把View对象返回给框架,适配器 
  22.            return imageView; 
  23.        } 
  24.  
  25.        // 3.销毁条目,其实就是将要销毁的对象object从container中移除出去就好了 
  26.        @Override 
  27.        public void destroyItem(ViewGroup container, int position, Object object) { 
  28.            container.removeView((View) object); 
  29.        } 
  30.  
  31.        // 4.指定复用的判断逻辑(一般为固定写法) 
  32.        @Override 
  33.        public boolean isViewFromObject(View view, Object object) { 
  34.            // 当滑动到新的条目之后,又返回回来,view是否可以被复用 
  35.            return view == object; 
  36.        } 
  37.    }  

在这里我们实现的是伪无限循环,其实就是将我们的第一项设置为一个很大的数的中间位置(Integer.MAX_VALUE),这样当我们向左向右滑动的时候,将返回的position对数据的大小进行取模运算%,根据相应的位置设置相应的图片或者文字即可。这样就实现了向右向左都是无限循环了。

4.实现自动播放

我们当然可以直接开启一个线程,在里面设置ViewPager的当前项,但是直接使用Handler更便于进行控机制,因为我们可以为ViewPager设置滚动监听器。自动播放控制代码:


  
  
  1. private void startRun() { 
  2.         mHandler = new Handler(); 
  3.         mHandler.postDelayed(mTaskRunnable, delayMillis); 
  4.  
  5.     } 
  6.  
  7.     //该线程一直运行着,知道activity被销毁,此时将isActivityAlive设置为false 
  8.     final Runnable mTaskRunnable = new Runnable() { 
  9.         @Override 
  10.         public void run() { 
  11.             // 如果activity未被销毁,就一直执行该线程 
  12.             // 在ViewPager的OnPageChangeListener方法中决定是否将isAutoRun置反 
  13.             if (isActivityAlive) { 
  14.                 if (isAutoRun) { 
  15.                     viewPager.setCurrentItem((viewPager.getCurrentItem() + 1) % imageViewList.size()); 
  16.                     mHandler.postDelayed(mTaskRunnable, delayMillis); 
  17.                 } else { 
  18.                     mHandler.postDelayed(mTaskRunnable, delayMillis); 
  19.                 } 
  20.             } 
  21.         } 
  22.     };  

为ViewPager设置滚动监听器,这里我们直接让当前类实现ViewPager.OnPageChangeListener接口,实现其中的抽象方法:


  
  
  1. @Override 
  2.     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
  3.  
  4.     } 
  5.  
  6.     @Override 
  7.     public void onPageSelected(int position) { 
  8.         int newPosition = position % imageViewList.size(); 
  9.         textView.setText(contentDescs[newPosition]); 
  10.  
  11.         // 先将上一个置位false,将当前位置置位true,这样可以使得初始化的时候就在第一个位置 
  12.         // (因为previousSelectedItem的未赋值时候的初始值默认为0) 
  13.         mPointsLayout.getChildAt(previousSelectedItem).setEnabled(false); 
  14.         mPointsLayout.getChildAt(newPosition).setEnabled(true); 
  15.         previousSelectedItem = newPosition; 
  16.     } 
  17.  
  18.     @Override 
  19.     public void onPageScrollStateChanged(int state) { 
  20.         switch (state) { 
  21.             // 静止状态 
  22.             case SCROLL_STATE_IDLE: 
  23.                 isAutoRun = true
  24.                 break; 
  25.             // 拖拽中 
  26.             case SCROLL_STATE_DRAGGING: 
  27.                 isAutoRun = false
  28.                 break; 
  29.             // 拖拽后松手,自动回到最终位置的过程 
  30.             case SCROLL_STATE_SETTLING: 
  31.                 isAutoRun = true
  32.                 break; 
  33.         } 
  34.     }  

这里面我们就实现了当手指滑动的时候停止自动播放,当手指抬起的时候又开始了自动播放。但是,注意,你以为这些代码组合起来就能实现循环滚动的同时还能手指控制是否自动播放?好吧,我当时也是这样以为的,运行后会报错:


  
  
  1. java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first 

意思是我们待加入的子视图已经有了一个父视图,需要调用它的父视图的removeView()方法来将其移除,之后再添加,然而事实证明并没有什么卵用。留待以后解决吧,在只能牺牲掉无线循环了,这里我们需要将getCount方法中的返回值改成我们数据的大小就好了:


  
  
  1. @Override 
  2.        public int getCount() { 
  3.  
  4.            return imageViewList.size(); 
  5.           // return Integer.MAX_VALUE; 
  6.        }  

然后将之前设置的默认的第一项:


  
  
  1. private void initAdapter() { 
  2.         int firstPosition = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % imageViewList.size()); 
  3.         //viewPager.setOffscreenPageLimit(imageViewList.size()); 
  4.         viewPager.setAdapter(new MyAdapter()); 
  5.         // 设置从中间的某个位置开始滑动,从而能够实现向左向右的循环滑动 
  6.         viewPager.setCurrentItem(firstPosition); 
  7.     }  

改为:


  
  
  1. private void initAdapter() { 
  2.  
  3.        viewPager.setAdapter(new MyAdapter()); 
  4.        // 设置从中间的某个位置开始滑动,从而能够实现向左向右的循环滑动 
  5.        viewPager.setCurrentItem(0); 
  6.    }  

可能说了这么多,看的都混了,在最后就贴一下完整的源码吧(支持自动播放、手指控制,但是不支持左右无限循环)。


  
  
  1. package com.qc.admin.mylunbotu; 
  2.  
  3. import android.os.Bundle; 
  4. import android.os.Handler; 
  5. import android.support.v4.view.PagerAdapter; 
  6. import android.support.v4.view.ViewPager; 
  7. import android.support.v7.app.AppCompatActivity; 
  8. import android.view.View
  9. import android.view.ViewGroup; 
  10. import android.widget.ImageView; 
  11. import android.widget.LinearLayout; 
  12. import android.widget.TextView; 
  13.  
  14. import java.util.ArrayList; 
  15. import java.util.List; 
  16.  
  17. import static android.support.v4.view.ViewPager.SCROLL_STATE_DRAGGING; 
  18. import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE; 
  19. import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING; 
  20.  
  21.  
  22. public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener { 
  23.  
  24.  
  25.     private ViewPager viewPager; 
  26.     private int[] imageResIds; 
  27.     private List<ImageView> imageViewList; 
  28.     private LinearLayout mPointsLayout; 
  29.     private String[] contentDescs; 
  30.     private int previousSelectedItem; 
  31.     private TextView textView; 
  32.     private Handler mHandler; 
  33.     boolean isAutoRun = true
  34.     boolean isActivityAlive = true
  35.     private int delayMillis = 2000; 
  36.  
  37.     @Override 
  38.     protected void onCreate(Bundle savedInstanceState) { 
  39.         super.onCreate(savedInstanceState); 
  40.         setContentView(R.layout.activity_main); 
  41.  
  42.         //初始化视图 
  43.         initViews(); 
  44.  
  45.         //初始化数据 
  46.         initData(); 
  47.  
  48.         //初始化适配器 
  49.         initAdapter(); 
  50.  
  51.         //开始自动播放 
  52.         startRun(); 
  53.  
  54.     } 
  55.  
  56.     private void startRun() { 
  57.         mHandler = new Handler(); 
  58.         mHandler.postDelayed(mTaskRunnable, delayMillis); 
  59.  
  60.     } 
  61.  
  62.     //该线程一直运行着,知道activity被销毁,此时将isActivityAlive设置为false 
  63.     final Runnable mTaskRunnable = new Runnable() { 
  64.         @Override 
  65.         public void run() { 
  66.             // 如果activity未被销毁,就一直执行该线程 
  67.             // 在ViewPager的OnPageChangeListener方法中决定是否将isAutoRun置反 
  68.             if (isActivityAlive) { 
  69.                 if (isAutoRun) { 
  70.                     viewPager.setCurrentItem((viewPager.getCurrentItem() + 1) % imageViewList.size()); 
  71.                     mHandler.postDelayed(mTaskRunnable, delayMillis); 
  72.                 } else { 
  73.                     mHandler.postDelayed(mTaskRunnable, delayMillis); 
  74.                 } 
  75.             } 
  76.         } 
  77.     }; 
  78.  
  79.     private void initAdapter() { 
  80.         //int firstPosition = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % imageViewList.size()); 
  81.         //viewPager.setOffscreenPageLimit(imageViewList.size()); 
  82.         viewPager.setAdapter(new MyAdapter()); 
  83.         // 设置从中间的某个位置开始滑动,从而能够实现向左向右的循环滑动 
  84.         //viewPager.setCurrentItem(firstPosition); 
  85.         viewPager.setCurrentItem(0); 
  86.     } 
  87.  
  88.     private void initData() { 
  89.  
  90.         //图片资源id数组 
  91.         imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e}; 
  92.         // 文本描述 
  93.         contentDescs = new String[]{ 
  94.                 "巩俐不低俗,我就不能低俗"
  95.                 "扑树又回来啦!再唱经典老歌引万人大合唱"
  96.                 "揭秘北京电影如何升级"
  97.                 "乐视网TV版大派送"
  98.                 "热血屌丝的反杀" 
  99.         }; 
  100.         imageViewList = new ArrayList<>(); 
  101.         LinearLayout.LayoutParams mParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); 
  102.         mParams.leftMargin = 15; 
  103.         mParams.topMargin = 2; 
  104.  
  105.         ImageView imageView; 
  106.         ImageView pointView; 
  107.         //初始化要展示的ImageView,并添加圆点指示器 
  108.         for (int i = 0; i < imageResIds.length; i++) { 
  109.             // 初始化图片 
  110.             imageView = new ImageView(this); 
  111.             imageView.setBackgroundResource(imageResIds[i]); 
  112.             imageViewList.add(imageView); 
  113.  
  114.             // 初始化指示器 
  115.             pointView = new ImageView(this); 
  116.             pointView.setBackgroundResource(R.drawable.point_bg); 
  117.             pointView.setLayoutParams(mParams); 
  118.             if (i == 0) { 
  119.                 textView.setText(contentDescs[0]); 
  120.                 pointView.setEnabled(true); 
  121.             } else { 
  122.                 pointView.setEnabled(false); 
  123.             } 
  124.             mPointsLayout.addView(pointView); 
  125.         } 
  126.     } 
  127.  
  128.     private void initViews() { 
  129.  
  130.         viewPager = (ViewPager) findViewById(R.id.viewpager); 
  131.         viewPager.addOnPageChangeListener(this); 
  132.  
  133.         textView = (TextView) findViewById(R.id.tv_desc); 
  134.         // 用来添加圆点指示器 
  135.         mPointsLayout = (LinearLayout) findViewById(R.id.point_container); 
  136.     } 
  137.  
  138.     @Override 
  139.     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
  140.  
  141.     } 
  142.  
  143.     @Override 
  144.     public void onPageSelected(int position) { 
  145.         int newPosition = position % imageViewList.size(); 
  146.         textView.setText(contentDescs[newPosition]); 
  147.  
  148.         // 先将上一个置位false,将当前位置置位true,这样可以使得初始化的时候就在第一个位置 
  149.         // (因为previousSelectedItem的未赋值时候的初始值默认为0) 
  150.         mPointsLayout.getChildAt(previousSelectedItem).setEnabled(false); 
  151.         mPointsLayout.getChildAt(newPosition).setEnabled(true); 
  152.         previousSelectedItem = newPosition; 
  153.     } 
  154.  
  155.     @Override 
  156.     public void onPageScrollStateChanged(int state) { 
  157.         switch (state) { 
  158.             // 静止状态 
  159.             case SCROLL_STATE_IDLE: 
  160.                 isAutoRun = true
  161.                 break; 
  162.             // 拖拽中 
  163.             case SCROLL_STATE_DRAGGING: 
  164.                 isAutoRun = false
  165.                 break; 
  166.             // 拖拽后松手,自动回到最终位置的过程 
  167.             case SCROLL_STATE_SETTLING: 
  168.                 isAutoRun = true
  169.                 break; 
  170.         } 
  171.     } 
  172.  
  173.     // 创建一个MyAdapter类,继承自PagerAdapter来为ViewPager设置适配器 
  174.     class MyAdapter extends PagerAdapter { 
  175.  
  176.         // 1.返回条目的总数 
  177.         @Override 
  178.         public int getCount() { 
  179.  
  180.             return imageViewList.size(); 
  181. //            return Integer.MAX_VALUE; 
  182.         } 
  183.  
  184.         // 2.返回要显示的条目,并创建条目 
  185.         @Override 
  186.         public Object instantiateItem(ViewGroup container, int position) { 
  187.             //container:容器,其实也就是ViewPager 
  188.             //position:当前要显示的条目的位置 
  189.  
  190.             int newPosition = position % imageViewList.size(); 
  191.             ImageView imageView = imageViewList.get(newPosition); 
  192.             //a.将View对象添加到container容器中 
  193.             container.addView(imageView); 
  194.             //b.把View对象返回给框架,适配器 
  195.             return imageView; 
  196.         } 
  197.  
  198.         // 3.销毁条目,其实就是将要销毁的对象object从container中移除出去就好了 
  199.         @Override 
  200.         public void destroyItem(ViewGroup container, int position, Object object) { 
  201.             container.removeView((View) object); 
  202.         } 
  203.  
  204.         // 4.指定复用的判断逻辑(一般为固定写法) 
  205.         @Override 
  206.         public boolean isViewFromObject(View view, Object object) { 
  207.             // 当滑动到新的条目之后,又返回回来,view是否可以被复用 
  208.             return view == object; 
  209.         } 
  210.     } 
  211.  
  212.     @Override 
  213.     protected void onDestroy() { 
  214.         isActivityAlive = false
  215.         super.onDestroy(); 
  216.     } 
  217.  





作者:i_seek_u
来源:51CTO
目录
相关文章
|
数据安全/隐私保护
RadioGroup+ViewPager +Fragment 制作APP主界面底部导航和左右滑动
RadioGroup+ViewPager +Fragment 制作APP主界面底部导航和左右滑动
133 0
RadioGroup+ViewPager +Fragment 制作APP主界面底部导航和左右滑动
|
移动开发 Android开发
仿淘宝、京东拖拽商品详情(可嵌套ViewPager、ListView、WebView、FragmentTabhost)
仿淘宝、京东拖拽商品详情(可嵌套ViewPager、ListView、WebView、FragmentTabhost)
244 0
仿淘宝、京东拖拽商品详情(可嵌套ViewPager、ListView、WebView、FragmentTabhost)
2-VIII--ViewPager滑动监听与自定义滑动特效
零、前言 [1]. 使用上文项目:1-VIII--ViewPager的基本使用 [2].对ViewPager的addOnPageChangeListener三个回调方法分析 [3].
1272 0
|
Java Android开发
ViewPager通过自定义适配器MyPagerAdapter实现界面导航(上标题)
ViewPager ViewPager的使用技巧其实有很多,这里只是我个人 平时学习得到的经验,希望对大家有所帮助,如有不对还请见谅。 很乐意接纳大家的意见与建议,本人邮箱: 893239524@qq.com
6724 0
|
容器
ViewPager 实现 Galler 效果, 中间大图显示,两边小图展示(优化篇)
上一张效果图: 之前的项目有一个Galley的项目,但是代码结构特别乱,别问我为什么,我也是刚接手这个项目,为了方便以后阅读和维护我对一些模块进行了重构。ViewPager实现Galler效果,但是当时时间比较急,写的比较仓促,上一篇实现了简单的效果,但是对于初始的时候左边滑动是有问题的,这是因为我们在自己的Adapter的时候对于getCount,我们想通过Integer.MAX_VAL
1713 0