一直在寻求一个能用得长久的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. sublime Text 正则替换

    我遇到一个文章,需要把所有的 (数字) 换为 [数字] 于是我使用 Sublime Text的替换 首先,我们需要打开正则使用"Alt+R" 或打开"Ctrl+h&quo ...

  2. js 关于性能的数据存储

    1.JavaScript中四种基本数据存取位置:字面量,本地变量,数组元素,对象成员.(四个都是存在于内存中) 一般来说:[字面量,局部变量]运行速度>[数组,对象成员] 2.内部属性包含了一个 ...

  3. Spark 作业调度相关术语

    作业(Job):RDD 中由行动操作所生成的一个或多个调度阶段 调度阶段(Stage):每个作业会因为 RDD 间的依赖关系拆分成多组任务集合,称为调度阶段,也叫做任务集(TaskSet).高度阶段的 ...

  4. JAVA基础知识总结:六

    一.不定长参数 1.语法:数据类型... 变量名称 使用注意事项:a.不定长参数就相当于是一个数组 b.不定长参数只能出现在参数列表的最后面 c.一个函数的参数列表中只能出现一次不定长参数 d.对于不 ...

  5. LeetCode 346. Moving Average from Data Stream (数据流动中的移动平均值)$

    Given a stream of integers and a window size, calculate the moving average of all integers in the sl ...

  6. Python之qq邮件

    用python发送电子邮件验证这个功能,可以说花费了我很久时间,这也是为什么我重头敲了之前的两遍代码.不要说我傻,仅仅是笨了一点而已 ^.- . 之前我所参考是如何发送 GMail. 但是我并不想这么 ...

  7. Bluetooth Obex

    OPP 1.2 which uses OBEX over L2CAP. OPP 1.1 connection and transfer happens over RFCOMM->L2CAP.

  8. 前端菜鸟学习之DOM事件处理

    一.事件处理程序 1.DOM0级事件处理程序:就是将一个函数赋值给一个事件处理程序属性,至今仍为现代所有浏览器所支持,主要得益于其跨浏览器的优势,要使用DOM0级事件 首先要得到操作对象的引用,具体实 ...

  9. Eddy's picture(最小生成树)

    Eddy's picture Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Tota ...

  10. Android 开发笔记___SQLite__基本用法

    SQLiteOpenHelper package com.example.alimjan.hello_world.dataBase; import android.content.ContentVal ...