[关闭]
@TryLoveCatch 2018-10-19T14:58:06.000000Z 字数 3873 阅读 2069

ViewPager实现引导页

android 自定义View 实例 ViewPager

需求

实现一个引导页,当ViewPager滑动一段时间之后,背景大图跟着滑动。这样我们组合前面的两篇文章:
超大图片处理
ViewPager背景渐变

思路

我们在ViewPager背景渐变这个基础上来开发,只需要增加大图的滑动就可以了。
参考ViewPager背景渐变关于OnPageChangeListener的介绍,想法就是:在onPageSelected()调用的时候,我们执行大图动画,这样就可以实现上面说的需求了。

  1. @Override
  2. public void onPageSelected(int position) {
  3. Log.e("MainActivity", "onPageSelected====position: " + position);
  4. handleDots(position);
  5. mIsPositionChanged = true;
  6. handleBcIconAnim(mCurrentPosition);
  7. }
  1. private void handleBcIconAnim(int pPosition){
  2. if (mScreenWidth <= 0) {
  3. return;
  4. }
  5. ValueAnimator tAnimator;
  6. if (pPosition > mLastPosition) {
  7. tAnimator = ValueAnimator.ofFloat(0f, 1f);
  8. } else {
  9. tAnimator = ValueAnimator.ofFloat(0f, -1f);
  10. }
  11. tAnimator.setDuration(650);
  12. tAnimator.setInterpolator(new DecelerateInterpolator());
  13. tAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  14. @Override
  15. public void onAnimationUpdate(ValueAnimator animation) {
  16. float tFloat = (float) animation.getAnimatedValue();
  17. float tMoveX = tFloat * mScreenWidth/2;
  18. mLargeImageView.move(Math.round(tMoveX - mLastMove), 0);
  19. mLastMove = tMoveX;
  20. }
  21. });
  22. tAnimator.addListener(new Animator.AnimatorListener() {
  23. @Override
  24. public void onAnimationStart(Animator animation) {
  25. mLastMove = 0;
  26. }
  27. @Override
  28. public void onAnimationEnd(Animator animation) {
  29. mLastMove = 0;
  30. }
  31. });
  32. tAnimator.start();
  33. mLastPosition = pPosition;
  34. }

动画也很简单,就是每次最多移动屏幕宽度的二分之一,然后根据mLastPositionpPosition来判断是左滑还是右滑,决定动画的取值,然后调用
mLargeImageView.move()即可了。效果如下:

跟咱们需求的基本上一直,但是看效果图,如果快速滑动的时候,后面的大图动画会一卡一卡的,这个肯定是因为动画没有执行完,所以我就想,如果动画正在执行,我就不让ViewPager响应滑动事件。
所以我增加一个布尔值,判断动画是否在执行,然后mViewPager.setOnTouchListener(),返回这个布尔值即可,如下:

  1. mViewPager.setOnTouchListener(new View.OnTouchListener() {
  2. @Override
  3. public boolean onTouch(View v, MotionEvent event) {
  4. Log.e("MainActivity", "setOnTouchListener====");
  5. return mIsAnim;
  6. }
  7. });

效果如下:

好了,解决了快速滑动的问题。但是。。。。当滑动的瞬间,我们朝相反的方向滑动,就会停在两个pager中间的位置。其实这个问题,仔细想想也对,因为mIsAnim为true,所以我们拦截了ViewPager的事件,就造成了这个问题。
怎么解决呢?我想的是,我在根布局拦截事件,动画执行的过程中,根本不让事件下发到ViewPager,应该就能解决这个问题了。代码如下:

  1. @Override
  2. public boolean dispatchTouchEvent(MotionEvent ev) {
  3. Log.e("MainActivity", "dispatchTouchEvent====");
  4. if(mIsAnim){
  5. return true;
  6. }
  7. return super.dispatchTouchEvent(ev);
  8. }

我重写了ActivitydispatchTouchEvent()方法,当动画过程中,拦截,否则走原有逻辑。
这样基本上没问题了,算是一个圆满的结局吧。

其他问题

适配问题

上面提到了,我们每次最多移动屏幕的一半,但是这个就有问题,比如我们的大图宽度是2000px,屏幕宽度是1080px,然后有5张page,我每次移动540,这样显示最后一张page的时候,大图不会有动画了,因为已经到头了。
所以需要动态算一下,每次可以移动的最大值,算法也很简单,如下:

  1. mMoveWidth = (mLargeImageView.getImageWidth() - getScreenWidth(this)) / (mArrViews.size() - 1);

图片的宽度-屏幕的宽度,得到剩下的宽度,然后其他几个page平分即可。

PageTransformer

本来下载了这里,但是写的比较多,所以专门另起了一篇文章,链接如下:
ViewPage动画PageTransformer分析

直接上代码,如下:

  1. private class MyTransformer implements ViewPager.PageTransformer {
  2. @Override
  3. public void transformPage(View page, float position) {
  4. if (position < -1) { // [-Infinity,-1)
  5. Log.e("MainActivity", "============" + position);
  6. } else if (position <= 1) { // [-1,1]
  7. float scrollXOffset = page.getWidth() * 1.5f;
  8. ViewGroup tViewGroup = (ViewGroup) page;
  9. View tView;
  10. for (int i = 0; i < tViewGroup.getChildCount(); i++) {
  11. tView = tViewGroup.getChildAt(i);
  12. if (tView == null || tView instanceof ImageView) {
  13. continue;
  14. }
  15. tView.setTranslationX(scrollXOffset * position);
  16. // scrollXOffset *= 0.5;
  17. }
  18. } else { // (1,+Infinity]
  19. Log.e("MainActivity", "============" + position);
  20. }
  21. }
  22. }

这里需要注意,我在之前的文章里面也说过

setTranslationX(0)会恢复TranslationX的值

所以我们利用这个特性,确保在动画结束的时候,setTranslationX()的参数传入的为0,这样布局就还原了。

横屏问题

横屏的时候,大图没有刷新,还是显示为竖屏的宽度,这个就需要我们自己刷新一次了,代码如下:

Activity监听onConfigurationChanged()

  1. @Override
  2. public void onConfigurationChanged(Configuration newConfig) {
  3. super.onConfigurationChanged(newConfig);
  4. mMoveWidth = (mLargeImageView.getImageWidth() - getScreenWidth(this)) / (mArrViews.size() - 1);
  5. if (mLargeImageView != null) {
  6. mLargeImageView.onConfigurationChanged(mCurrentPosition, mMoveWidth);
  7. }
  8. }
  1. public void onConfigurationChanged(int pPosition, int pMoveWidth) {
  2. mIsFirstMeasure = true;
  3. mPosition = pPosition;
  4. mMoveWidth = pMoveWidth;
  5. }

因为有可能,我是在第二页才横屏的,所以需要传position参数。

效果如下

代码地址

ViewPagerDemo

Demo下载

Demo下载

参考

实现知乎 Android 客户端启动页视差滚动效果
ViewPager 从入门到带你撸个启动页之实战PageTransformer切换动画特效(四)
Android ViewPager切换之PageTransformer接口中transformPage方法position参数使用详解
用PageTranformer实现更炫酷的动画

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注