首页 技术 正文
技术 2022年11月6日
0 收藏 624 点赞 496 浏览 8103 个字

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

注:并非完全原创

先看一下效果:

淡入淡出:

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);
} }
}
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,487
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,903
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,736
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,486
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,126
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,287