一直在寻求一个能用得长久的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. UWP应用程序使用Prism框架构建MVVM

    在我们创建的UWP解决方案中选择引用->管理NuGet包程序包 NuGet管理包 2. 搜索Prism.Core,并安装 搜索Prism.Core 3. 搜索Prism.Unity,并安装 搜索 ...

  2. eval函数的用法

    可以把list,tuple,dict和string相互转化. ################################################# 字符串转换成列表 >>&g ...

  3. Traefik实现Kubernetes集群服务外部https访问

    转载请注明出处:http://www.cnblogs.com/wayneiscoming/p/7707942.html traefik 是一个前端http反向代理服务器以及负载均衡器,支持多种微服务后 ...

  4. 文本可视化[二]——《今生今世》人物关系可视化python实现

    文本可视化[二]--<今生今世>人物关系可视化python实现 在文本可视化[一]--<今生今世>词云生成与小说分析一文中,我使用了jieba分词和wordcloud实现了,文 ...

  5. SGU 223 Little Kings(状压DP)

    Description 用字符矩阵来表示一个8x8的棋盘,'.'表示是空格,'P'表示人质,'K'表示骑士.每一步,骑士可以移动到他周围的8个方格中的任意一格.如果你移动到的格子中有人质(即'P'), ...

  6. NodeJs通过镜像下载相关NPM模块

    临时通过镜像使用一次:npm --registry https://registry.npm.taobao.org install  模块名[设置镜像源地址为淘宝] 持久使用的第一种方法: npm c ...

  7. 从零起步学python计划及感想

    从纯传统bi转型过来的技术顾问,比较有优势的是对业务的熟悉,对数据有敏感度,熟悉数据模型.但是长年累月基本都是用sql处理问题.目前还没有经历过sql解决不了的问题,一个sql解决不了就用临时表,几个 ...

  8. VUE 源码学习01 源码入口

    VUE[version:2.4.1] Vue项目做了不少,最近在学习设计模式与Vue源码,记录一下自己的脚印!共勉!注:此处源码学习方式为先了解其大模块,从宏观再去到微观学习,以免一开始就研究细节然后 ...

  9. Vue源码终笔-VNode更新与diff算法初探

    写完这个就差不多了,准备干新项目了. 确实挺不擅长写东西,感觉都是罗列代码写点注释的感觉,这篇就简单阐述一下数据变动时DOM是如何更新的,主要讲解下其中的diff算法. 先来个正常的html模板: & ...

  10. 从架构演进的角度聊聊Spring Cloud都做了些什么?

    Spring Cloud作为一套微服务治理的框架,几乎考虑到了微服务治理的方方面面,之前也写过一些关于Spring Cloud文章,主要偏重各组件的使用,本次分享主要解答这两个问题:Spring Cl ...