一直在寻求一个能用得长久的ViewPager,寻寻觅觅终于发现,ViewPager有这一个就够了。

注:并非完全原创

先看一下效果:

淡入淡出:

旋转:

无限轮播的ViewPager

主要设计思路(以ViewPager右滑为例):

  • 无限轮播:通过Handler负责View的切换,每次向自身发送延迟消息,达到无限循环的目的;当处于ViewPager最后一个时候,继续向右滑,调用setCurrentItem,回到第一个。
  • 其中很关键的点,就是最后一个Item回到第一个的时候,默认动画太难看了,因此,需要将首尾两个Item设置成相同的,在首尾切换的时候,将动画暂时取消掉,以用户不易察觉的情况,偷偷切回第一个Item。
  • 自动切换动画的速度设置:通过Java反射机制获取父类的Scroller参数,在子类中创建我们自己的Scroller,然后再透过反射,将我们自己的Scroll塞给父类,这样就可以通过Scroller设置切换动画的速度。
  1. /**
  2. * 自动轮播的ViewPager
  3. * Created by ChenSS on 2017/1/5.
  4. */
  5. public class MylViewPager extends ViewPager {
  6. public static final int DEFAULT_INTERVAL = 1500;
  7. public static final int LEFT = 0;
  8. public static final int RIGHT = 1;
  9. /**
  10. * 什么也不做,当滑动在最后一项或者第一项,什么也不做
  11. **/
  12. public static final int SLIDE_BORDER_MODE_NONE = 0;
  13. /**
  14. * 当滑动在最后一项或者第一项,开启循环,这种模式下,当滑到最后一个Item回到第一个Item,
  15. * 这种模式下,最后一个Item和第一个Item相同的话会显得自然一些
  16. **/
  17. public static final int SLIDE_BORDER_MODE_CYCLE = 1;
  18. /**
  19. * 当滑动在最后一项或者第一项,开启循环,并且阻止事件拦截
  20. **/
  21. public static final int SLIDE_BORDER_MODE_TO_PARENT = 2;
  22. /**
  23. * 自动滚动的时间,以毫秒为单位
  24. **/
  25. private long interval = DEFAULT_INTERVAL;
  26. /**
  27. * 自动滚动方向, default is {@link #RIGHT}
  28. **/
  29. private int direction = RIGHT;
  30. /**
  31. * 是否自动循环
  32. **/
  33. private boolean isCycle = true;
  34. /**
  35. * 接触时是否停止自动滚动
  36. **/
  37. private boolean stopScrollWhenTouch = true;
  38. /**
  39. * 如何处理当滑动在最后还是第一项
  40. **/
  41. private int slideBorderMode = SLIDE_BORDER_MODE_NONE;
  42. /**
  43. * 动画时是否自动滚动在最后或第一项取消动画
  44. **/
  45. private boolean isBorderAnimation = false;
  46. private Handler handler;
  47. private boolean isAutoScroll = false;
  48. private boolean isStopByTouch = false;
  49. private float touchX = 0f, downX = 0f;
  50. private CustomDurationScroller scroller = null;
  51. public static final int SCROLL_WHAT = 0;
  52. public MylViewPager(Context paramContext) {
  53. super(paramContext);
  54. init();
  55. }
  56. public MylViewPager(Context paramContext, AttributeSet paramAttributeSet) {
  57. super(paramContext, paramAttributeSet);
  58. init();
  59. }
  60. private void init() {
  61. handler = new MyHandler();
  62. setViewPagerScroller();
  63. }
  64. public void setSlideBorderMode(int slideBorderMode) {
  65. this.slideBorderMode = slideBorderMode;
  66. }
  67. public void setDirection(int direction) {
  68. this.direction = direction;
  69. }
  70. /**
  71. * 启动动画
  72. */
  73. public void startAutoScroll() {
  74. isAutoScroll = true;
  75. sendScrollMessage(interval);
  76. }
  77. /**
  78. * 启动动画
  79. */
  80. public void startAutoScroll(int delayTimeInMills) {
  81. isAutoScroll = true;
  82. sendScrollMessage(delayTimeInMills);
  83. }
  84. /**
  85. * 停止动画
  86. */
  87. public void stopAutoScroll() {
  88. isAutoScroll = false;
  89. handler.removeMessages(SCROLL_WHAT);
  90. }
  91. /**
  92. * 设置动画切换的时间
  93. */
  94. public void setScrollDurationFactor(double scrollFactor) {
  95. scroller.setScrollDurationFactor(scrollFactor);
  96. }
  97. private void sendScrollMessage(long delayTimeInMills) {
  98. handler.removeMessages(SCROLL_WHAT);
  99. handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills);
  100. }
  101. /**
  102. * 利用反射机制修改滚动的时间
  103. */
  104. private void setViewPagerScroller() {
  105. try {
  106. Field scrollerField = ViewPager.class.getDeclaredField("mScroller");
  107. scrollerField.setAccessible(true);
  108. //插值器
  109. Field interpolatorField = ViewPager.class.getDeclaredField("sInterpolator");
  110. interpolatorField.setAccessible(true);
  111. scroller = new CustomDurationScroller(getContext(), (Interpolator) interpolatorField.get(null));
  112. scrollerField.set(this, scroller);
  113. } catch (Exception e) {
  114. e.printStackTrace();
  115. }
  116. }
  117. /**
  118. * 选择滑动到哪一页
  119. */
  120. public void scrollOnce() {
  121. PagerAdapter adapter = getAdapter();
  122. int currentItem = getCurrentItem();
  123. int totalCount;
  124. if (adapter == null || (totalCount = adapter.getCount()) <= 1) {
  125. return;
  126. }
  127. int nextItem = (direction == LEFT) ? --currentItem : ++currentItem;
  128. if (nextItem < 0) {
  129. if (isCycle) {
  130. setCurrentItem(totalCount - 1, isBorderAnimation);
  131. //如果是第一个就0延迟
  132. sendScrollMessage(0);
  133. }
  134. } else if (nextItem == totalCount) {
  135. if (isCycle) {
  136. setCurrentItem(0, isBorderAnimation);
  137. //如果是最后一个就0延迟
  138. sendScrollMessage(0);
  139. }
  140. } else {
  141. //默认情况就按照设定的时间切换图片
  142. setCurrentItem(nextItem, true);
  143. sendScrollMessage(interval);
  144. }
  145. }
  146. @Override
  147. public boolean onTouchEvent(MotionEvent ev) {
  148. if (stopScrollWhenTouch) {
  149. if (ev.getAction() == MotionEvent.ACTION_DOWN && isAutoScroll) {
  150. isStopByTouch = true;
  151. stopAutoScroll();
  152. } else if (ev.getAction() == MotionEvent.ACTION_UP && isStopByTouch) {
  153. startAutoScroll();
  154. }
  155. }
  156. if (slideBorderMode == SLIDE_BORDER_MODE_TO_PARENT || slideBorderMode == SLIDE_BORDER_MODE_CYCLE) {
  157. touchX = ev.getX();
  158. if (ev.getAction() == MotionEvent.ACTION_DOWN) {
  159. downX = touchX;
  160. }
  161. int currentItem = getCurrentItem();
  162. PagerAdapter adapter = getAdapter();
  163. int pageCount = adapter == null ? 0 : adapter.getCount();
  164. if ((currentItem == 0 && downX <= touchX) || (currentItem == pageCount - 1 && downX >= touchX)) {
  165. //事件拦截
  166. if (slideBorderMode == SLIDE_BORDER_MODE_TO_PARENT) {
  167. getParent().requestDisallowInterceptTouchEvent(false);
  168. } else {
  169. if (pageCount > 1) {
  170. setCurrentItem(pageCount - currentItem - 1, isBorderAnimation);
  171. }
  172. getParent().requestDisallowInterceptTouchEvent(true);
  173. }
  174. return super.onTouchEvent(ev);
  175. }
  176. }
  177. getParent().requestDisallowInterceptTouchEvent(true);
  178. return super.onTouchEvent(ev);
  179. }
  180. private class MyHandler extends Handler {
  181. @Override
  182. public void handleMessage(Message msg) {
  183. super.handleMessage(msg);
  184. switch (msg.what) {
  185. case SCROLL_WHAT:
  186. scrollOnce();
  187. //下面的方法,Handler向自身发送延迟消息
  188. //sendScrollMessage(interval);
  189. default:
  190. break;
  191. }
  192. }
  193. }
  194. class CustomDurationScroller extends Scroller {
  195. private double scrollFactor = 1;
  196. public CustomDurationScroller(Context context, Interpolator interpolator) {
  197. super(context, interpolator);
  198. }
  199. /**
  200. * 设置滚动切换的时间
  201. */
  202. public void setScrollDurationFactor(double scrollFactor) {
  203. this.scrollFactor = scrollFactor;
  204. }
  205. @Override
  206. public void startScroll(int startX, int startY, int dx, int dy, int duration) {
  207. super.startScroll(startX, startY, dx, dy, (int) (duration * scrollFactor));
  208. }
  209. }
  210. }

自定义ViewPager动画

首先你要实现ViewPager.PageTransformer接口,你需要设计transformPage(View view, float position)的方法体,第一个参数是具体的Item,第二个参数是Item的position,也就是Item的位置。

position是一个极为关键的参数, 当我们的View显示在屏幕上的时候,position的值是-1到1。

  • 当position大于-1小于0时,表示View正在从左侧滑入(也可能滑出,如果ViewPager滑的方向换掉);
  • 当等于0,表示View刚刚好显示在屏幕中,但是通常获取不到这个刚刚等零的值;
  • 当position大于0小于1,表示View正在从右侧滑出(也可能滑入,如果ViewPager滑的方向换掉)。

淡入淡出的ViewPager切换动画

  1. /**
  2. * ViewPager的滑动特效
  3. * Created by ChenSS on 2017/1/5.
  4. */
  5. public class AlphaPageTransformer implements ViewPager.PageTransformer {
  6. /**
  7. * 以阿尔法值为例,这个是0-1的值,可以设置View的透明度
  8. */
  9. private float alpha;
  10. /**
  11. * 在这里编辑你ViewPager的动画特效
  12. *
  13. * @param view 拿到你ViewPager的Item的View
  14. * @param position View现在所处的位置
  15. */
  16. @Override
  17. public void transformPage(View view, float position) {
  18. if (position < -1) {
  19. //当它消失在左边,通常还原最普通的状态,设置Alpha为1
  20. ViewHelper.setAlpha(view, 1);
  21. } else if (position <= 0) {
  22. //左滑特效,这个时候它的position在屏幕左侧,所以是负值
  23. alpha = 1 - (0 - position);
  24. ViewHelper.setAlpha(view, alpha);
  25. } else if (position <= 1) {
  26. //右滑特效,这个时候它的position在屏幕上,所以是正值
  27. alpha = 1 - position;
  28. ViewHelper.setAlpha(view, alpha);
  29. } else {
  30. //当它消失在右边,通常还原最普通的状态,设置Alpha为1
  31. ViewHelper.setAlpha(view, 1);
  32. }
  33. }
  34. }

旋转的ViewPager切换动画

  1. /**
  2. * 旋转
  3. * Created by ChenSS on 2017/1/5.
  4. */
  5. public class RotatePageTransformer implements ViewPager.PageTransformer {
  6. private static final float BASE_ANGLE = 10.0f;
  7. private float angle;
  8. public void transformPage(View view, float position) {
  9. if (position < -1) {
  10. ViewHelper.setRotation(view, 0);
  11. } else if (position <= 0) {
  12. angle = (BASE_ANGLE * position);
  13. ViewHelper.setPivotX(view, view.getMeasuredWidth() * 0.5f);
  14. ViewHelper.setPivotY(view, view.getMeasuredWidth() * 0.5f);
  15. ViewHelper.setRotation(view, angle);
  16. } else if (position <= 1) {
  17. angle = (BASE_ANGLE * position);
  18. ViewHelper.setPivotX(view, view.getMeasuredWidth() * 0.5f);
  19. ViewHelper.setPivotY(view, view.getMeasuredWidth() * 0.5f);
  20. ViewHelper.setRotation(view, angle);
  21. } else {
  22. ViewHelper.setRotation(view, 0);
  23. }
  24. }
  25. }

测试代码

代码我没全贴出来,Activity的布局文件只有ViewPager,然后Fragment的布局文件只有ImageView,比较简单。

  1. public class MaterialDesignViewPagerActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_material_design_view_pager);
  6. MylViewPager viewPager = (MylViewPager) findViewById(R.id.md_view_pager_asvp);
  7. ArrayList<Fragment> fragmentList = new ArrayList<>();
  8. //注意首尾的Item是一样的
  9. fragmentList.add(new RecyclerViewBean(R.mipmap.b_1));
  10. fragmentList.add(new RecyclerViewBean(R.mipmap.b_2));
  11. fragmentList.add(new RecyclerViewBean(R.mipmap.b_3));
  12. fragmentList.add(new RecyclerViewBean(R.mipmap.b_1));
  13. RecyclerVpAdapter bAdapter = new RecyclerVpAdapter(getSupportFragmentManager(), fragmentList);
  14. viewPager.setAdapter(bAdapter);
  15. viewPager.setCurrentItem(0);
  16. //自动滑动方向往左
  17. viewPager.setDirection(MylViewPager.LEFT);
  18. //开启无限循环的MODE
  19. viewPager.setSlideBorderMode(MylViewPager.SLIDE_BORDER_MODE_CYCLE);
  20. //设置自定义Alpha切换动画效果
  21. viewPager.setPageTransformer(true, new AlphaPageTransformer());
  22. //设置动画切换的速度
  23. viewPager.setScrollDurationFactor(10);
  24. //开启自动轮播,设置每次切换的时间间隔
  25. viewPager.startAutoScroll(2000);
  26. }
  27. class RecyclerVpAdapter extends FragmentPagerAdapter {
  28. ArrayList<Fragment> list;
  29. public RecyclerVpAdapter(FragmentManager fragmentManager, ArrayList<Fragment> list) {
  30. super(fragmentManager);
  31. this.list = list;
  32. }
  33. @Override
  34. public int getCount() {
  35. return list.size();
  36. }
  37. @Override
  38. public Fragment getItem(int arg0) {
  39. return list.get(arg0);
  40. }
  41. }
  42. }

ViewPager无限轮播与自定义切换动画的更多相关文章

  1. 踩石行动:ViewPager无限轮播的坑

    2016-6-19 前言 View轮播效果在app中很常见,一想到左右滑动的效果就很容易想到使用ViewPager来实现.对于像我们常说的banner这样的效果,具备无限滑动的功能是可以用ViewPa ...

  2. ViewPager +无限轮播+滑动速度修改+指示小点

    养成习惯,做过代码记录总结. ViewPager 使用记录 1. ViewPage 位于V4包. 2.主要用来做banner轮播. 3.原理:适配器重用提高效率,与listview等一个原理. 下面记 ...

  3. ViewPager实现无限轮播踩坑记

    最近笔者想通过ViewPager来实现一个广告Banner,并实现无限轮播的效果,但是在这个过程中踩了不少的坑,听我慢慢道来.如果大家有遇到和我一样的情况,可以参考我的解决方法,没有那就更好,如果针对 ...

  4. Android使用ViewPager做轮播

    ViewPager.html div.oembedall-githubrepos { border: 1px solid #DDD; list-style-type: none; margin: 0 ...

  5. Android真正意义上的无限轮播Banner

    在android开发的时候,经常会使用到轮播图,对于这种效果,一般情况下,我们都会使用一种叫做ViewPager的来实现. 传统的实现逻辑是自定义一个View继承ViewPager,在适配器中 将co ...

  6. Android之仿京东淘宝的自动无限轮播控件

    在App的开发中,很多的时候都需要实现类似京东淘宝一样的自动无限轮播的广告栏,所以就自己写了一个,下面是我自定义控件的思路和过程. 一.自定义控件属性 新建自定义控件SliderLayout继承于Re ...

  7. Android 轮播图Banner切换图片的效果

    Android XBanner使用详解 2018年03月14日 08:19:59 AND_Devil 阅读数:910   版权声明:本文为博主原创文章,未经博主允许不得转载. https://www. ...

  8. iOS:实现图片的无限轮播(二)---之使用第三方库SDCycleScrollView

    iOS:实现图片的无限轮播(二)---之使用第三方库SDCycleScrollView 时间:2016-01-19 19:13:43      阅读:630      评论:0      收藏:0   ...

  9. iOS开发UI篇—无限轮播(功能完善)

    iOS开发UI篇—无限轮播(功能完善) 一.自动滚动 添加并设置一个定时器,每个2.0秒,就跳转到下一条. 获取当前正在展示的位置. [self addNSTimer]; } -(void)addNS ...

随机推荐

  1. PHP连接mysql数据库进行增删改查--删除

    删除: 1.首页 在foreach里面加入   <td><a href='dele.php?id={$i[0]}'>删除</a></td> 在上面< ...

  2. 记一次vscode升级后,格式化Vue出现的问题

    一.VSCode中使用vetur插件格式化vue文件时,stylus代码会自动加上大括号.冒号和分号 本来就是简写比较方便舒服,结果一个格式化回到十年前 解决方案: vscode  文件 ->首 ...

  3. 【转】 bio 与块设备驱动

    原文地址: bio 与块设备驱动      系统中能够随机访问固定大小数据片(chunk)的设备被称作块设备,这些数据片就称作块.块设备文件都是以安装文件系统的方式使用,此也是块设备通常的访问方式.块 ...

  4. 移动端通过ajax上传图片(文件)并在前台展示——通过H5的FormData对象

    前些时候遇到移动端需要上传图片和视频的问题,之前一直通过ajax异步的提交数据,所以在寻找通过ajax上传文件的方法.发现了H5里新增了一个FormData对象,通过这个对象可以直接绑定html中的f ...

  5. swift之函数式编程(二)

    本文的主要内容来自<Functional Programming in Swift>这本书,有点所谓的观后总结 在本书的Introduction章中: we will try to foc ...

  6. 基于Spring、SpringMVC、MyBatis、Druid、Shrio构建web系统

    源码下载地址:https://github.com/shuaijunlan/Autumn-Framework 在线Demo:http://autumn.shuaijunlan.cn 项目介绍 Autu ...

  7. Leetcode题解(十三)

    36.Valid Sudoku 题目 代码如下: class Solution { public: bool isValidSudoku(vector<vector<char> &g ...

  8. Leetcode题解(七)

    24.Swap Nodes in Pairs 题目 看到此题,第一想法是利用两个指针,分别将其所指向的节点的value交换.然后同时向后移动2个节点,代码如下: struct ListNode { i ...

  9. Spring AOP分析(2) -- JdkDynamicAopProxy实现AOP

    上文介绍了代理类是由默认AOP代理工厂DefaultAopProxyFactory中createAopProxy方法产生的.如果代理对象是接口类型,则生成JdkDynamicAopProxy代理:否则 ...

  10. Problem E: 动物爱好者

    Problem E: 动物爱好者 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 882  Solved: 699[Submit][Status][Web ...