使用Viewpager实现幻灯片播放功能
最近要由于项目需要,要实现一个支持幻灯片播放功能的
图片轮播功能,自己在实现过程中也做了些总结,现在记录下来,以便之后复看,也希望可以帮到一些需要帮助的人吧。
基本的思路
总的来说实现幻灯片播放的思路如下:
- 1 完成动画效果
- 2 完成定时切换
1.动画切换效果
原理解析
完成动画切换效果的方法是通过ViewPager的这个函数实现:ViewPager.setPageTransformer(boolean reverseDrawingOrder,ViewPager.PageTransformer transformer);
我们先来看看这个方法的一些介绍:
Sets a {@link PageTransformer} that will be called for each attached page whenever
* the scroll position is changed. This allows the application to apply custom property
* transformations to each page, overriding the default sliding behavior.
大体意思就是说当ViewPager滑动时,设置的PageTransformer中的对应方法会被调用,使得应用可以使用自己的方式来实现不同的页面切换效果
那么这个动画切换效果的具体实现该怎么写呢,我们来看看ViewPager
.PageTransformer接口定义的方法:
public interface PageTransformer {
/**
* Apply a property transformation to the given page.
*
* @param page Apply the transformation to this page
* @param position Position of page relative to the current front-and-center
* position of the pager. 0 is front and center. 1 is one full
* page position to the right, and -1 is one page position to the left.
*/
void transformPage(View page, float position);
}
简单来说,就是当一个页面完全处于中间时,position的大小为0,当页面从中间向左滑动直到刚好消失时,position的变化为0 ->-1,当页面从中间向右滑动到刚好消失时,position的变化为0 -> 1。这样子我们就可以通过position的变化,在不同的position时将当前的page设置不同的属性(例如setTranslation,setRotation,setAlpha等),达到各种各样的页面切换效果.
一些实现的效果:
- 立方体旋转效果:
@Override
public void transformPage(View page, float position) {
//终极无敌史诗级例外的操作 position < -1 || position > 1
if ((position < -1) || (position > 1)){
return;
}
//刚好不可见 复原
if ((position == -1) ||(position == 1)){
resetPage(page);
return;
}
//从左边出去
if (position <= 0) {
page.setPivotX(page.getWidth());
page.setPivotY(page.getHeight() * 0.5f);
page.setRotationY(90f * position);
return;
}
//从右边出去
if (position < 1){
page.setPivotX(0);
page.setPivotY(page.getWidth() * 0.5f);
page.setRotationY(90f * position);
return;
}
}
/**
* 恢复page的属性
* @param page 指定的view
*/
void resetPage(View page){
//设置旋转中心点;
page.setPivotX(page.getWidth());
page.setPivotY(page.getHeight() * 0.5f);
//只在Y轴做旋转操作
page.setRotationY(0);
}
- 中心旋转效果
@Override
public void transformPage(View page, float position) {
int pageWidth = page.getWidth();
//终极无敌史诗级例外的操作 position < -1 || position > 1
if ((position < -1) || (position > 1)){
return;
}
//刚好不可见 复原
if ((position == -1) || (position == 1)){
resetPage(page);
return;
}
//从左边出去
if (position <= 0){
//阻止消失界面的移动
page.setTranslationX(-pageWidth * position);
final float rotation = -180f * position;
page.setPivotX(page.getWidth() * 0.5f);
page.setPivotY(page.getHeight() * 0.5f);
page.setRotation(rotation);
float alphaFactor = Math.max(0.0f, 1 - Math.abs(position));
//透明度改变
page.setAlpha(alphaFactor);
return;
}
//从右边出去
if (position < 1){
//阻止出现的界面的移动
page.setTranslationX(-pageWidth * position);
final float rotation = -180f * position;
page.setPivotX(page.getWidth() * 0.5f);
page.setPivotY(page.getHeight() * 0.5f);
page.setRotation(rotation);
float alphaFactor = Math.max(0.0f, 1 - Math.abs(position));
//透明度改变
page.setAlpha(alphaFactor);
return;
}
}
/**
* 恢复page的属性
* @param page 当前page
*/
void resetPage(View page){
page.setAlpha(1.0f);
page.setTranslationX(0);
page.setPivotX(0);
page.setPivotY(0);
page.setRotation(0.0f);
}
- 缩小平移效果
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;
@Override
public void transformPage(View page, float position) {
int pageWidth = page.getWidth();
int pageHeight = page.getHeight();
//终极无敌史诗级例外的操作 position < -1 || position > 1
if ((position < -1) || (position > 1)){
return;
}
//刚好不可见 复原
if ((position == -1) || (position == 1)){
resetPage(page);
return;
}
//从左边出去
if (position <= 0){
float scaleFactor = Math.max(MIN_SCALE,1-Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horMargin = pageWidth * (1 - scaleFactor) / 2;
//左移
page.setTranslationX(horMargin - vertMargin / 2);
// Scale the page down (between MIN_SCALE and 1)
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
// Fade the page relative to its size.
page.setAlpha(MIN_ALPHA +
(scaleFactor - MIN_SCALE) /
(1 - MIN_SCALE) * (1 - MIN_ALPHA));
return;
}
//从右边出去
if (position < 1){
float scaleFactor = Math.max(MIN_SCALE,1-Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horMargin = pageWidth * (1 - scaleFactor) / 2;
page.setTranslationX(-horMargin + vertMargin / 2);
// Scale the page down (between MIN_SCALE and 1)
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
// Fade the page relative to its size.
page.setAlpha(MIN_ALPHA +
(scaleFactor - MIN_SCALE) /
(1 - MIN_SCALE) * (1 - MIN_ALPHA));
return;
}
}
/**
* 恢复page的属性
* @param page 当前page
*/
void resetPage(View page){
page.setAlpha(1.0f);
page.setTranslationX(0.0f);
page.setScaleX(1.0f);
page.setScaleY(1.0f);
}
- 旋转向下
@Override
public void transformPage(View page, float position) {
final float MAX_ROTATE_ANGLE = 20.0f;
//终极无敌史诗级例外的操作 position < -1 || position > 1
if ((position < -1) || (position > 1)){
return;
}
//刚好不可见 复原
if ((position == -1) || (position == 1)){
resetPage(page);
return;
}
//从左边出去
if (position <= 0){
float angle = MAX_ROTATE_ANGLE * position;
page.setPivotX(page.getWidth() * 0.5f);
page.setPivotY(page.getHeight());
page.setRotation(angle);
return;
}
//从右边出去
if (position < 1){
float angle = MAX_ROTATE_ANGLE * position;
page.setPivotX(page.getWidth() * 0.5f);
page.setPivotY(page.getHeight());
page.setRotation(angle);
return;
}
}
/**
* 恢复page的属性
* @param page 当前page
*/
void resetPage(View page){
page.setPivotX(page.getWidth() * 0.5f);
page.setPivotY(page.getHeight());
page.setRotation(0);
}
- 放大进入
@Override
public void transformPage(View page, float position) {
//终极无敌史诗级例外的操作 position < -1 || position > 1
if ((position < -1) || (position > 1)){
return;
}
//刚好不可见 复原
if ((position == -1)){
page.setPivotX(0);
page.setPivotY(page.getHeight() * 0.5f);
page.setScaleX(1.0f);
page.setScaleY(1.0f);
return;
}
//刚好不可见 复原
if (position == 1){
page.setPivotX(page.getWidth());
page.setPivotY(page.getHeight() * 0.5f);
page.setScaleX(1.0f);
page.setScaleY(1.0f);
return;
}
//从左边出去
if (position <= 0){
page.setPivotX(0);
page.setPivotY(page.getHeight() * 0.5f);
float scale = 1.0f + position;
page.setScaleX(scale);
page.setScaleY(scale);
return;
}
//从右边出去
if (position < 1){
page.setPivotX(page.getWidth());
page.setPivotY(page.getHeight() * 0.5f);
float scale = 1.0f - position;
page.setScaleX(scale);
page.setScaleY(scale);
return;
}
}
2.定时切换
这个实际上还蛮好实现的,我当时选择的实现方式是使用Timer + Handler的实现方式,主要是通过使用
Timer.scheduleAtFixedRate(TimerTask task, long delay, long period),通过设定period长短控制播放速度,在Timerask中发送message,通知Handler执行切换,完成幻灯片播放效果.
3.一些坑的地方
1.幻灯片播放功能中有切换动画的设置功能,之前在实现的时候,在切换了动画之后,当前page前后的两个page都没有复原,导致切换效果很奇怪,后来就在page变为不可见之后,将其所有属性复原,即可。
2.当进入到幻灯片播放模式之后,当前页面的切换动画总是一闪而过,导致切换效果很难受,所以查阅资料之后,发现可以通过修改ViewPager的mScroller的实现,来指定每一次scroll的时延。以下是解决方法:
/**
* 使用反射设置viewpager的页面切换时间
* @param scrollDuration 切换时长
* @param isReset 是否恢复原来的效果 true:恢复 false:不恢复 一般为true
*/
private void setScrollDuration(final int scrollDuration, final boolean isReset){
LogUtils.d(TAG, "setScrollDuration: duration: " + scrollDuration);
try {
Field field = ViewPager.class.getDeclaredField("mScroller");
field.setAccessible(true);
Scroller mScroller = new Scroller(this,new LinearInterpolator()){
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
if (isReset){
super.startScroll(startX, startY, dx, dy);
}else{
super.startScroll(startX, startY, dx, dy,scrollDuration);
}
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
if (isReset){//由系统控制
super.startScroll(startX, startY, dx, dy, duration);
}else{//幻灯片播放
super.startScroll(startX, startY, dx, dy, scrollDuration);
}
}
};
field.set(mPictureViewPager,mScroller);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e){
e.printStackTrace();
}
}
上面的实现都很清晰了,就不做介绍了。
好了,这篇文章就这么愉快地结束了
参考资料: