在淘宝等电商的APP首页常常能看到大幅的广告位,一般有多幅常常更新的图片用于展现促销信息,以下图所示:html
一般会自动滚动,也能够根据手势滑动。我没有研究过人家的APP是经过什么实现的,可能有第三方已经封装好的控件能够直接使用,也可能经过webview来实现,毕竟在网页上也有不少相似的内容。若是有高手经验丰富不妨指点一二。无论别人怎样,今天我准备本身动手作一个,其实也不是特别复杂的。android
我主要使用的实现方法是Android自带的ViewPager控件,这个控件主要用于实现屏幕水平切换,有自带的动画效果。Android官网上有使用教程:http://developer.android.com/training/animation/screen-slide.html,固然下文会有相一致的说明。web
而后就开始动手实现这个图片滑动效果吧。网络
1. 首先解释一下ViewPager的使用过程,一般来讲ViewPager能够和Fragment结合起来用,每次ViewPager滑动,也就是展现一个新的Fragment。Fragment里面就是咱们须要展示的内容。而这滑动的动画就交给ViewPager来实现。因此第一步咱们须要设置好Fragment的内容,新建一个布局文件,这里只须要展现一张图便可,固然能够根据本身的须要进行改变:app
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/iv_main_pic" android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@string/main_image" android:scaleType="fitXY" /> </RelativeLayout>
2. 而后创建Fragment类。ide
import android.support.v4.app.Fragment; ...... public class PictureSlideFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_picture_slide, container, false); return v; } }
3. 在Activity布局文件的合适位置加入ViewPager控件。布局
<android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent" />
4. 在Activity中获得这个ViewPager而且为其设置Adapter:动画
private ViewPager mPager;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPager = (ViewPager) findViewById(R.id.pager); mPagerAdapter = new PictureSlidePagerAdapter(getSupportFragmentManager()); mPager.setAdapter(mPagerAdapter); }
5. 这个Adapter继承自FragmentStatePagerAdapter,其中getCount()返回的值是一共须要显示的内容数,是个常数:spa
private class PictureSlidePagerAdapter extends FragmentStatePagerAdapter { public PictureSlidePagerAdapter(FragmentManager fm) { super(fm); // TODO Auto-generated constructor stub } @Override public Fragment getItem(int arg0) { // TODO Auto-generated method stub return new PictureSlideFragment(); } @Override public int getCount() { // TODO Auto-generated method stub return NUM_PIC; } }
6. 到目前为止全部的内容都和官方的教程一致。若是你在Fragment中的那个ImageView经过android:src属性设置图片,会实现数张静态图片滑动的效果。这离咱们的目标还有一些区别:线程
(1)图片须要可以动态改变,而不是固定的内容;
(2)每张图片须要有点击的响应;
(3)通常状况下须要实现循环滚动,即滑到最后一张图时继续滑动会回到第一张图;
(4)图片要可以自动滚动;
(5)图片下方须要有显示第几张图的指示(小圆点)。
7. 接下来咱们朝着目标继续努力。首先是图片的变化。观察上述Adapter的实现方法,能够发现getItem()方法每次返回的都是一个Fragment的实例,须要显示多少个Fragment这个方法就会执行多少遍。但咱们发现每次建立新的Fragment都没有区别,直接new一个了事,所以咱们须要改写这个建立新Fragment实例的方法,以实现每次新建的Fragment实例都不同。在咱们的Fragment类中补充以下内容:
private int mIndex; public static PictureSlideFragment newInstance(int index) { PictureSlideFragment f = new PictureSlideFragment(); Bundle args = new Bundle(); args.putInt("index", index); f.setArguments(args); return f; } @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); mIndex = getArguments() != null ? getArguments().getInt("index") : 1; }
这个叫作newInstance的方法主要功能也是建立一个Fragment实例,和直接new的区别是传递进来一个index参数,这个参数在onCreate()方法中被得到。而后咱们将Adapter的getItem()改写为以下:
@Override public Fragment getItem(int arg0) { // TODO Auto-generated method stub return PictureSlideFragment.newInstance(arg0); }
每次getItem都将Item的序号传递到新建的Fragment,而后再Fragment中根据须要设定不一样的内容。真正实现的时候,能够在Fragment中加入从网络获取图片的操做。
8. 图片的点击响应。这个比较简单,在Fragment中得到ImageView控件,而后设置onClick监听器便可。就不给出代码了。
9. 图片的循环滚动。通常的ViewPager不能实现内容的循环滚动,即第一张向左滑或者最后一张向右滑都会到顶,显示到顶的效果。若是要从ViewPager自身入手加入这功能须要对这个控件进行改写,比较麻烦,这里我从stackoverflow上找到了一种比较聪明的办法。
要实现几张图的循环滚动,其实只须要知足视觉效果就能够了。好比有三张图A,B,C,要实现A->B->C->A->B......以及C->B->A->C->B...这样的循环,实际上能够这样实现:
提供5张图,分别是C' A B C A',其中C'和C,A'和A彻底相同,当滑到C'时,自动切换到C,当滑到A'时,自动切换到A,而且这个切换过程没有动画,因而,使用者体验出来的感受就是A B C三张图在循环滚动了。
因而咱们为ViewPager添加setOnPageChangeListener方法:
mPager.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int arg0) { // TODO Auto-generated method stub } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { // TODO Auto-generated method stub } @Override public void onPageScrollStateChanged(int state) { // 当到第一张时切换到倒数第二张,当到最后一张时切换到第二张 if (state == ViewPager.SCROLL_STATE_IDLE) { int curr = mPager.getCurrentItem(); int lastReal = mPager.getAdapter().getCount() - 2; if (curr == 0) { mPager.setCurrentItem(lastReal, false); } else if (curr > lastReal) { mPager.setCurrentItem(1, false); } } } });
覆写的onPageScrollStateChanged方法在滑动内容改变时调用,在其中实现C'和C,A'和A的切换。
10. 图片的自动滚动,这里我使用了timer启动线程来实现,定时器线程每隔5秒发送消息,UI线程捕获消息并调用ViewPager.setCurrentItem(currentItem+1, true)方法,滚动到下一张图片。代码就不赘述了。这里有个体验细节,就是当使用者用手指滑动时,定时器须要取消,防止自动滑动和手指滑动发生冲突。这个我在Fragment的ImageView的onTouchListener中实现:
mMainImage.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { ((MainActivity)getActivity()).getTimer().cancel(); } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { ((MainActivity)getActivity()).startSwitchImage(); } return false; } });
11. 图片序号的指示,也就是图片下方的几个小圆点,这个实现比较简单,首先在Activity的布局文件中添加几个图片,由于这几个小圆点自己不能随ViewPager移动,所以不能放在Fragment布局中。切换内容也就是切换显示不一样颜色小圆点的位置,这个方法一样能够在onPageScrollStateChanged方法中调用。注意一些细节,由于以前为了实现循环滚动而多用了两个Fragment,而显示的小圆点数量不能增长,所以须要调整好对应的序号。代码也再也不赘述了。
最终的效果以下图所示,美食有木有。总结一下,整个过程并不复杂,就是一些细节须要仔细推敲。虽然作法可能有非主流的地方,但实现的效果跟别人的几乎彻底一致,这样就行啦~
另外,这样的实现方法也不是彻底没有值得改进的地方,好比图片自动滚动的延时还不能自定义。ViewPager没有现成的方法用来自改变自动滚动延时,若是须要自定义,就要本身改写这个控件啦,有兴趣的能够研究一下~