轮播广告在如今的应用中比较常见,下面就来实现下该功能(文章参考了网上流传的黑马的视频教程)
先来看下具体的实现效果:java
实现思路:
1.为ViewPager设置数据源,实现ViewPager的滚动
2.将圆点指示器与ViewPager的页面对应起来
3.实现左右滑动均能无限循环
4.实现自动播放
5.实现当手指滑动的时候取消自动播发android
首先须要说明的就是在这篇文章里并无同时实现左右循环滑动和手指触碰自动中止的功能,这个问题尚未解决,留待之后再解决,也但愿有知道的朋友在下面留言,先谢谢拉。数组
这里咱们采用对的是相对布局RelativeLayout,固然也能够采用帧布局FrameLayout。布局文件activity_mian.xml代码:app
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.qc.admin.mylunbotu.MainActivity"> <RelativeLayout android:layout_width="match_parent" android:layout_height="160dp"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v4.view.ViewPager> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp" android:layout_alignParentBottom="true" android:background="#66000000" android:gravity="center_horizontal" android:orientation="vertical" android:padding="5dp"> <TextView android:id="@+id/tv_desc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxLines="1" android:text="这是图片描述" android:textColor="@android:color/white" /> <!--用来填充圆点指示器的容器--> <LinearLayout android:id="@+id/point_container" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:orientation="horizontal" /> </LinearLayout> </RelativeLayout> </RelativeLayout>
这里咱们提供的图片数据和显示的文本数据都是静态的,直接从数组资源中得到的,如:框架
//图片资源id数组 imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e}; // 文本描述 contentDescs = new String[]{ "巩俐不低俗,我就不能低俗", "扑树又回来啦!再唱经典老歌引万人大合唱", "揭秘北京电影如何升级", "乐视网TV版大派送", "热血屌丝的反杀" };
咱们能够本身定义两个shape,来显示圆点指示器的两种不一样显示状态,而后再定义一个selector,将它设置为控件的背景(ImageView),经过其setEnable(Boolean bool)方法能够自动的决定使用哪一个图片。代码分别以下:ide
a.正常状态下,灰色圆点,point_normal.xml:布局
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="5dp" android:height="5dp" /> <solid android:color="#44000000" /> </shape>
b.点击状态下,白色圆点 point_pressed.xml:post
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="5dp" android:height="5dp"/> <solid android:color="#FFFFFFFF"/> </shape>
c.设置背景时的xml文件, point_bg.xml:this
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="true" android:drawable="@drawable/point_pressed"/> <item android:state_enabled="false" android:drawable="@drawable/point_normal"/> </selector>
具体代码:spa
private void initData() { //图片资源id数组 imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e}; // 文本描述 contentDescs = new String[]{ "巩俐不低俗,我就不能低俗", "扑树又回来啦!再唱经典老歌引万人大合唱", "揭秘北京电影如何升级", "乐视网TV版大派送", "热血屌丝的反杀" }; imageViewList = new ArrayList<>(); LinearLayout.LayoutParams mParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); mParams.leftMargin = 15; mParams.topMargin = 2; ImageView imageView; ImageView pointView; //初始化要展现的ImageView,并添加圆点指示器 for (int i = 0; i < imageResIds.length; i++) { // 初始化图片 imageView = new ImageView(this); imageView.setBackgroundResource(imageResIds[i]); imageViewList.add(imageView); // 初始化指示器 pointView = new ImageView(this); pointView.setBackgroundResource(R.drawable.point_bg); pointView.setLayoutParams(mParams); if (i == 0) { textView.setText(contentDescs[0]); pointView.setEnabled(true); } else { pointView.setEnabled(false); } mPointsLayout.addView(pointView); } }
注意:建立shape文件的时候,若是找不到,能够切换到project目录下,在drawable目录下右键,new->Drawable resource file ,获得以下界面:
将selector直接改成shape便可
class MyAdapter extends PagerAdapter { // 1.返回条目的总数 @Override public int getCount() { //return imageViewList.size(); return Integer.MAX_VALUE; } // 2.返回要显示的条目,并建立条目 @Override public Object instantiateItem(ViewGroup container, int position) { //container:容器,其实也就是ViewPager //position:当前要显示的条目的位置 int newPosition = position % imageViewList.size(); ImageView imageView = imageViewList.get(newPosition); //a.将View对象添加到container容器中 container.addView(imageView); //b.把View对象返回给框架,适配器 return imageView; } // 3.销毁条目,其实就是将要销毁的对象object从container中移除出去就行了 @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } // 4.指定复用的判断逻辑(通常为固定写法) @Override public boolean isViewFromObject(View view, Object object) { // 当滑动到新的条目以后,又返回回来,view是否能够被复用 return view == object; } }
在这里咱们实现的是伪无限循环,其实就是将咱们的第一项设置为一个很大的数的中间位置(Integer.MAX_VALUE),这样当咱们向左向右滑动的时候,将返回的position对数据的大小进行取模运算%,根据相应的位置设置相应的图片或者文字便可。这样就实现了向右向左都是无限循环了。
咱们固然能够直接开启一个线程,在里面设置ViewPager的当前项,可是直接使用Handler更便于进行控机制,由于咱们能够为ViewPager设置滚动监听器。
自动播放控制代码:
private void startRun() { mHandler = new Handler(); mHandler.postDelayed(mTaskRunnable, delayMillis); } //该线程一直运行着,知道activity被销毁,此时将isActivityAlive设置为false final Runnable mTaskRunnable = new Runnable() { @Override public void run() { // 若是activity未被销毁,就一直执行该线程 // 在ViewPager的OnPageChangeListener方法中决定是否将isAutoRun置反 if (isActivityAlive) { if (isAutoRun) { viewPager.setCurrentItem((viewPager.getCurrentItem() + 1) % imageViewList.size()); mHandler.postDelayed(mTaskRunnable, delayMillis); } else { mHandler.postDelayed(mTaskRunnable, delayMillis); } } } };
为ViewPager设置滚动监听器,这里咱们直接让当前类实现ViewPager.OnPageChangeListener接口,实现其中的抽象方法:
@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { int newPosition = position % imageViewList.size(); textView.setText(contentDescs[newPosition]); // 先将上一个置位false,将当前位置置位true,这样可使得初始化的时候就在第一个位置 // (由于previousSelectedItem的未赋值时候的初始值默认为0) mPointsLayout.getChildAt(previousSelectedItem).setEnabled(false); mPointsLayout.getChildAt(newPosition).setEnabled(true); previousSelectedItem = newPosition; } @Override public void onPageScrollStateChanged(int state) { switch (state) { // 静止状态 case SCROLL_STATE_IDLE: isAutoRun = true; break; // 拖拽中 case SCROLL_STATE_DRAGGING: isAutoRun = false; break; // 拖拽后松手,自动回到最终位置的过程 case SCROLL_STATE_SETTLING: isAutoRun = true; break; } }
这里面咱们就实现了当手指滑动的时候中止自动播放,当手指抬起的时候又开始了自动播放。可是,注意,你觉得这些代码组合起来就能实现循环滚动的同时还能手指控制是否自动播放?好吧,我当时也是这样觉得的,运行后会报错:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
意思是咱们待加入的子视图已经有了一个父视图,须要调用它的父视图的removeView()方法来将其移除,以后再添加,然而事实证实并无什么卵用。留待之后解决吧,在只能牺牲掉无线循环了,这里咱们须要将getCount方法中的返回值改为咱们数据的大小就行了:
@Override public int getCount() { return imageViewList.size(); // return Integer.MAX_VALUE; }
而后将以前设置的默认的第一项:
private void initAdapter() { int firstPosition = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % imageViewList.size()); //viewPager.setOffscreenPageLimit(imageViewList.size()); viewPager.setAdapter(new MyAdapter()); // 设置从中间的某个位置开始滑动,从而可以实现向左向右的循环滑动 viewPager.setCurrentItem(firstPosition); }
改成:
private void initAdapter() { viewPager.setAdapter(new MyAdapter()); // 设置从中间的某个位置开始滑动,从而可以实现向左向右的循环滑动 viewPager.setCurrentItem(0); }
可能说了这么多,看的都混了,在最后就贴一下完整的源码吧(支持自动播放、手指控制,可是不支持左右无限循环)。
package com.qc.admin.mylunbotu; import android.os.Bundle; import android.os.Handler; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import java.util.ArrayList; import java.util.List; import static android.support.v4.view.ViewPager.SCROLL_STATE_DRAGGING; import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE; import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING; public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener { private ViewPager viewPager; private int[] imageResIds; private List<ImageView> imageViewList; private LinearLayout mPointsLayout; private String[] contentDescs; private int previousSelectedItem; private TextView textView; private Handler mHandler; boolean isAutoRun = true; boolean isActivityAlive = true; private int delayMillis = 2000; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化视图 initViews(); //初始化数据 initData(); //初始化适配器 initAdapter(); //开始自动播放 startRun(); } private void startRun() { mHandler = new Handler(); mHandler.postDelayed(mTaskRunnable, delayMillis); } //该线程一直运行着,知道activity被销毁,此时将isActivityAlive设置为false final Runnable mTaskRunnable = new Runnable() { @Override public void run() { // 若是activity未被销毁,就一直执行该线程 // 在ViewPager的OnPageChangeListener方法中决定是否将isAutoRun置反 if (isActivityAlive) { if (isAutoRun) { viewPager.setCurrentItem((viewPager.getCurrentItem() + 1) % imageViewList.size()); mHandler.postDelayed(mTaskRunnable, delayMillis); } else { mHandler.postDelayed(mTaskRunnable, delayMillis); } } } }; private void initAdapter() { //int firstPosition = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % imageViewList.size()); //viewPager.setOffscreenPageLimit(imageViewList.size()); viewPager.setAdapter(new MyAdapter()); // 设置从中间的某个位置开始滑动,从而可以实现向左向右的循环滑动 //viewPager.setCurrentItem(firstPosition); viewPager.setCurrentItem(0); } private void initData() { //图片资源id数组 imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e}; // 文本描述 contentDescs = new String[]{ "巩俐不低俗,我就不能低俗", "扑树又回来啦!再唱经典老歌引万人大合唱", "揭秘北京电影如何升级", "乐视网TV版大派送", "热血屌丝的反杀" }; imageViewList = new ArrayList<>(); LinearLayout.LayoutParams mParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); mParams.leftMargin = 15; mParams.topMargin = 2; ImageView imageView; ImageView pointView; //初始化要展现的ImageView,并添加圆点指示器 for (int i = 0; i < imageResIds.length; i++) { // 初始化图片 imageView = new ImageView(this); imageView.setBackgroundResource(imageResIds[i]); imageViewList.add(imageView); // 初始化指示器 pointView = new ImageView(this); pointView.setBackgroundResource(R.drawable.point_bg); pointView.setLayoutParams(mParams); if (i == 0) { textView.setText(contentDescs[0]); pointView.setEnabled(true); } else { pointView.setEnabled(false); } mPointsLayout.addView(pointView); } } private void initViews() { viewPager = (ViewPager) findViewById(R.id.viewpager); viewPager.addOnPageChangeListener(this); textView = (TextView) findViewById(R.id.tv_desc); // 用来添加圆点指示器 mPointsLayout = (LinearLayout) findViewById(R.id.point_container); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { int newPosition = position % imageViewList.size(); textView.setText(contentDescs[newPosition]); // 先将上一个置位false,将当前位置置位true,这样可使得初始化的时候就在第一个位置 // (由于previousSelectedItem的未赋值时候的初始值默认为0) mPointsLayout.getChildAt(previousSelectedItem).setEnabled(false); mPointsLayout.getChildAt(newPosition).setEnabled(true); previousSelectedItem = newPosition; } @Override public void onPageScrollStateChanged(int state) { switch (state) { // 静止状态 case SCROLL_STATE_IDLE: isAutoRun = true; break; // 拖拽中 case SCROLL_STATE_DRAGGING: isAutoRun = false; break; // 拖拽后松手,自动回到最终位置的过程 case SCROLL_STATE_SETTLING: isAutoRun = true; break; } } // 建立一个MyAdapter类,继承自PagerAdapter来为ViewPager设置适配器 class MyAdapter extends PagerAdapter { // 1.返回条目的总数 @Override public int getCount() { return imageViewList.size(); // return Integer.MAX_VALUE; } // 2.返回要显示的条目,并建立条目 @Override public Object instantiateItem(ViewGroup container, int position) { //container:容器,其实也就是ViewPager //position:当前要显示的条目的位置 int newPosition = position % imageViewList.size(); ImageView imageView = imageViewList.get(newPosition); //a.将View对象添加到container容器中 container.addView(imageView); //b.把View对象返回给框架,适配器 return imageView; } // 3.销毁条目,其实就是将要销毁的对象object从container中移除出去就行了 @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } // 4.指定复用的判断逻辑(通常为固定写法) @Override public boolean isViewFromObject(View view, Object object) { // 当滑动到新的条目以后,又返回回来,view是否能够被复用 return view == object; } } @Override protected void onDestroy() { isActivityAlive = false; super.onDestroy(); } }