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

注:并非完全原创

先看一下效果:

淡入淡出:

旋转:

无限轮播的ViewPager

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

  • 无限轮播:通过Handler负责View的切换,每次向自身发送延迟消息,达到无限循环的目的;当处于ViewPager最后一个时候,继续向右滑,调用setCurrentItem,回到第一个。
  • 其中很关键的点,就是最后一个Item回到第一个的时候,默认动画太难看了,因此,需要将首尾两个Item设置成相同的,在首尾切换的时候,将动画暂时取消掉,以用户不易察觉的情况,偷偷切回第一个Item。
  • 自动切换动画的速度设置:通过Java反射机制获取父类的Scroller参数,在子类中创建我们自己的Scroller,然后再透过反射,将我们自己的Scroll塞给父类,这样就可以通过Scroller设置切换动画的速度。
/**
* 自动轮播的ViewPager
* Created by ChenSS on 2017/1/5.
*/
public class MylViewPager extends ViewPager {
public static final int DEFAULT_INTERVAL = 1500; public static final int LEFT = 0;
public static final int RIGHT = 1; /**
* 什么也不做,当滑动在最后一项或者第一项,什么也不做
**/
public static final int SLIDE_BORDER_MODE_NONE = 0;
/**
* 当滑动在最后一项或者第一项,开启循环,这种模式下,当滑到最后一个Item回到第一个Item,
* 这种模式下,最后一个Item和第一个Item相同的话会显得自然一些
**/
public static final int SLIDE_BORDER_MODE_CYCLE = 1;
/**
* 当滑动在最后一项或者第一项,开启循环,并且阻止事件拦截
**/
public static final int SLIDE_BORDER_MODE_TO_PARENT = 2; /**
* 自动滚动的时间,以毫秒为单位
**/
private long interval = DEFAULT_INTERVAL;
/**
* 自动滚动方向, default is {@link #RIGHT}
**/
private int direction = RIGHT;
/**
* 是否自动循环
**/
private boolean isCycle = true;
/**
* 接触时是否停止自动滚动
**/
private boolean stopScrollWhenTouch = true;
/**
* 如何处理当滑动在最后还是第一项
**/
private int slideBorderMode = SLIDE_BORDER_MODE_NONE;
/**
* 动画时是否自动滚动在最后或第一项取消动画
**/
private boolean isBorderAnimation = false; private Handler handler;
private boolean isAutoScroll = false;
private boolean isStopByTouch = false;
private float touchX = 0f, downX = 0f;
private CustomDurationScroller scroller = null; public static final int SCROLL_WHAT = 0; public MylViewPager(Context paramContext) {
super(paramContext);
init();
} public MylViewPager(Context paramContext, AttributeSet paramAttributeSet) {
super(paramContext, paramAttributeSet);
init();
} private void init() {
handler = new MyHandler();
setViewPagerScroller();
} public void setSlideBorderMode(int slideBorderMode) {
this.slideBorderMode = slideBorderMode;
} public void setDirection(int direction) {
this.direction = direction;
} /**
* 启动动画
*/
public void startAutoScroll() {
isAutoScroll = true;
sendScrollMessage(interval);
} /**
* 启动动画
*/
public void startAutoScroll(int delayTimeInMills) {
isAutoScroll = true;
sendScrollMessage(delayTimeInMills);
} /**
* 停止动画
*/
public void stopAutoScroll() {
isAutoScroll = false;
handler.removeMessages(SCROLL_WHAT);
} /**
* 设置动画切换的时间
*/
public void setScrollDurationFactor(double scrollFactor) {
scroller.setScrollDurationFactor(scrollFactor);
} private void sendScrollMessage(long delayTimeInMills) {
handler.removeMessages(SCROLL_WHAT);
handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills);
} /**
* 利用反射机制修改滚动的时间
*/
private void setViewPagerScroller() {
try {
Field scrollerField = ViewPager.class.getDeclaredField("mScroller");
scrollerField.setAccessible(true);
//插值器
Field interpolatorField = ViewPager.class.getDeclaredField("sInterpolator");
interpolatorField.setAccessible(true); scroller = new CustomDurationScroller(getContext(), (Interpolator) interpolatorField.get(null));
scrollerField.set(this, scroller);
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 选择滑动到哪一页
*/
public void scrollOnce() {
PagerAdapter adapter = getAdapter();
int currentItem = getCurrentItem();
int totalCount;
if (adapter == null || (totalCount = adapter.getCount()) <= 1) {
return;
} int nextItem = (direction == LEFT) ? --currentItem : ++currentItem;
if (nextItem < 0) {
if (isCycle) {
setCurrentItem(totalCount - 1, isBorderAnimation);
//如果是第一个就0延迟
sendScrollMessage(0);
}
} else if (nextItem == totalCount) {
if (isCycle) {
setCurrentItem(0, isBorderAnimation);
//如果是最后一个就0延迟
sendScrollMessage(0);
}
} else {
//默认情况就按照设定的时间切换图片
setCurrentItem(nextItem, true);
sendScrollMessage(interval);
}
} @Override
public boolean onTouchEvent(MotionEvent ev) {
if (stopScrollWhenTouch) {
if (ev.getAction() == MotionEvent.ACTION_DOWN && isAutoScroll) {
isStopByTouch = true;
stopAutoScroll();
} else if (ev.getAction() == MotionEvent.ACTION_UP && isStopByTouch) {
startAutoScroll();
}
} if (slideBorderMode == SLIDE_BORDER_MODE_TO_PARENT || slideBorderMode == SLIDE_BORDER_MODE_CYCLE) {
touchX = ev.getX();
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
downX = touchX;
}
int currentItem = getCurrentItem();
PagerAdapter adapter = getAdapter();
int pageCount = adapter == null ? 0 : adapter.getCount();
if ((currentItem == 0 && downX <= touchX) || (currentItem == pageCount - 1 && downX >= touchX)) {
//事件拦截
if (slideBorderMode == SLIDE_BORDER_MODE_TO_PARENT) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
if (pageCount > 1) {
setCurrentItem(pageCount - currentItem - 1, isBorderAnimation);
}
getParent().requestDisallowInterceptTouchEvent(true);
}
return super.onTouchEvent(ev);
}
}
getParent().requestDisallowInterceptTouchEvent(true);
return super.onTouchEvent(ev);
} private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg); switch (msg.what) {
case SCROLL_WHAT:
scrollOnce();
//下面的方法,Handler向自身发送延迟消息
//sendScrollMessage(interval);
default:
break;
}
}
} class CustomDurationScroller extends Scroller {
private double scrollFactor = 1; public CustomDurationScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
} /**
* 设置滚动切换的时间
*/
public void setScrollDurationFactor(double scrollFactor) {
this.scrollFactor = scrollFactor;
} @Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, (int) (duration * scrollFactor));
}
}
}

自定义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切换动画

/**
* ViewPager的滑动特效
* Created by ChenSS on 2017/1/5.
*/
public class AlphaPageTransformer implements ViewPager.PageTransformer {
/**
* 以阿尔法值为例,这个是0-1的值,可以设置View的透明度
*/
private float alpha; /**
* 在这里编辑你ViewPager的动画特效
*
* @param view 拿到你ViewPager的Item的View
* @param position View现在所处的位置
*/
@Override
public void transformPage(View view, float position) {
if (position < -1) {
//当它消失在左边,通常还原最普通的状态,设置Alpha为1
ViewHelper.setAlpha(view, 1);
} else if (position <= 0) {
//左滑特效,这个时候它的position在屏幕左侧,所以是负值
alpha = 1 - (0 - position);
ViewHelper.setAlpha(view, alpha);
} else if (position <= 1) {
//右滑特效,这个时候它的position在屏幕上,所以是正值
alpha = 1 - position;
ViewHelper.setAlpha(view, alpha);
} else {
//当它消失在右边,通常还原最普通的状态,设置Alpha为1
ViewHelper.setAlpha(view, 1);
}
}
}

旋转的ViewPager切换动画

/**
* 旋转
* Created by ChenSS on 2017/1/5.
*/ public class RotatePageTransformer implements ViewPager.PageTransformer {
private static final float BASE_ANGLE = 10.0f;
private float angle; public void transformPage(View view, float position) {
if (position < -1) {
ViewHelper.setRotation(view, 0);
} else if (position <= 0) {
angle = (BASE_ANGLE * position);
ViewHelper.setPivotX(view, view.getMeasuredWidth() * 0.5f);
ViewHelper.setPivotY(view, view.getMeasuredWidth() * 0.5f);
ViewHelper.setRotation(view, angle);
} else if (position <= 1) {
angle = (BASE_ANGLE * position);
ViewHelper.setPivotX(view, view.getMeasuredWidth() * 0.5f);
ViewHelper.setPivotY(view, view.getMeasuredWidth() * 0.5f);
ViewHelper.setRotation(view, angle);
} else {
ViewHelper.setRotation(view, 0);
}
}
}

测试代码

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

public class MaterialDesignViewPagerActivity extends AppCompatActivity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_material_design_view_pager); MylViewPager viewPager = (MylViewPager) findViewById(R.id.md_view_pager_asvp);
ArrayList<Fragment> fragmentList = new ArrayList<>();
//注意首尾的Item是一样的
fragmentList.add(new RecyclerViewBean(R.mipmap.b_1));
fragmentList.add(new RecyclerViewBean(R.mipmap.b_2));
fragmentList.add(new RecyclerViewBean(R.mipmap.b_3));
fragmentList.add(new RecyclerViewBean(R.mipmap.b_1)); RecyclerVpAdapter bAdapter = new RecyclerVpAdapter(getSupportFragmentManager(), fragmentList);
viewPager.setAdapter(bAdapter);
viewPager.setCurrentItem(0);
//自动滑动方向往左
viewPager.setDirection(MylViewPager.LEFT);
//开启无限循环的MODE
viewPager.setSlideBorderMode(MylViewPager.SLIDE_BORDER_MODE_CYCLE);
//设置自定义Alpha切换动画效果
viewPager.setPageTransformer(true, new AlphaPageTransformer());
//设置动画切换的速度
viewPager.setScrollDurationFactor(10);
//开启自动轮播,设置每次切换的时间间隔
viewPager.startAutoScroll(2000);
} class RecyclerVpAdapter extends FragmentPagerAdapter {
ArrayList<Fragment> list; public RecyclerVpAdapter(FragmentManager fragmentManager, ArrayList<Fragment> list) {
super(fragmentManager);
this.list = list; } @Override
public int getCount() {
return list.size();
} @Override
public Fragment getItem(int arg0) {
return list.get(arg0);
} }
}

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. Hadoop(九)Hadoop IO之Compression和Codecs

    前言 前面一篇介绍了Java怎么去查看数据块的相关信息和怎么去查看文件系统.我们只要知道怎么去查看就行了!接下来我分享的是Hadoop的I/O操作. 在Hadoop中为什么要去使用压缩(Compres ...

  2. ASP.NET Core 企业级开发架构简介及框架汇总

    企业开发框架包括垂直方向架构和水平方向架构.垂直方向架构是指一个应用程序的由下到上叠加多层的架构,同时这样的程序又叫整体式程序.水平方向架构是指将大应用分成若干小的应用实现系统功能的架构,同时这样的系 ...

  3. LeetCode 461. Hamming Distance (汉明距离)

    The Hamming distance between two integers is the number of positions at which the corresponding bits ...

  4. Python爬虫之爬取慕课网课程评分

    BS是什么? BeautifulSoup是一个基于标签的文本解析工具.可以根据标签提取想要的内容,很适合处理html和xml这类语言文本.如果你希望了解更多关于BS的介绍和用法,请看Beautiful ...

  5. 对Spring事务一些问题的讨论

    提起spring事务,就会让人联想起四大基本特征,五个隔离级别,七大传播特性.相信大多数人都知道这些东西,但是知道是一回事情,能用好真的是另一回事了.在使用Spring事务的时候,我曾遇到过几个比较严 ...

  6. swift之函数式编程(五)

    文章内容来源于<Functional Programing in Swift>,详情请看原著 The Value of Immutability swift 对于控制值改变有一些机制.在这 ...

  7. 小程序组件之picker和range-key的用法

        因为在微信小程序的官网上并没有range-key的例子以及实际用法,所以好多人不知道具体如何使用.然后我在这里对其进行一个简单的实现,并记录一些注意事项. 以下是官网给的说明:   具体的用法 ...

  8. Leetcode题解(十)

    29.Divide Two Integers 题目 题目要求不用乘除和取模运算,实现两个整数相除: 我的第一想法就是把除法变成减法来做,这也是最初除法的定义,其实现代码如下: class Soluti ...

  9. grid 布局 CSS3

    display:grid 是一种新的布局方式,旧的布局方式通常有副作用,例如float(需要额外修复浮动)或者inline-block(两个元素之间的空格问题)   把父元素定义为grid,就像表格一 ...

  10. vue初级学习--环境搭建

    一.导语 最近总想学点东西,es6啊.typescript啊,都想学,刚好有个机遇,可以学点vue,嗯,那就开始吧. 二.正文 1.node环境: 下载安装nodeJs,最好是1.6以上的版本,下载地 ...