@TryLoveCatch
2018-10-19T06:58:06.000000Z
字数 3873
阅读 2308
android 自定义View 实例 ViewPager
实现一个引导页,当ViewPager滑动一段时间之后,背景大图跟着滑动。这样我们组合前面的两篇文章:
超大图片处理
ViewPager背景渐变
我们在ViewPager背景渐变这个基础上来开发,只需要增加大图的滑动就可以了。
参考ViewPager背景渐变关于OnPageChangeListener的介绍,想法就是:在onPageSelected()调用的时候,我们执行大图动画,这样就可以实现上面说的需求了。
@Overridepublic void onPageSelected(int position) {Log.e("MainActivity", "onPageSelected====position: " + position);handleDots(position);mIsPositionChanged = true;handleBcIconAnim(mCurrentPosition);}
private void handleBcIconAnim(int pPosition){if (mScreenWidth <= 0) {return;}ValueAnimator tAnimator;if (pPosition > mLastPosition) {tAnimator = ValueAnimator.ofFloat(0f, 1f);} else {tAnimator = ValueAnimator.ofFloat(0f, -1f);}tAnimator.setDuration(650);tAnimator.setInterpolator(new DecelerateInterpolator());tAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float tFloat = (float) animation.getAnimatedValue();float tMoveX = tFloat * mScreenWidth/2;mLargeImageView.move(Math.round(tMoveX - mLastMove), 0);mLastMove = tMoveX;}});tAnimator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {mLastMove = 0;}@Overridepublic void onAnimationEnd(Animator animation) {mLastMove = 0;}});tAnimator.start();mLastPosition = pPosition;}
动画也很简单,就是每次最多移动屏幕宽度的二分之一,然后根据mLastPosition和pPosition来判断是左滑还是右滑,决定动画的取值,然后调用
mLargeImageView.move()即可了。效果如下:

跟咱们需求的基本上一直,但是看效果图,如果快速滑动的时候,后面的大图动画会一卡一卡的,这个肯定是因为动画没有执行完,所以我就想,如果动画正在执行,我就不让ViewPager响应滑动事件。
所以我增加一个布尔值,判断动画是否在执行,然后mViewPager.setOnTouchListener(),返回这个布尔值即可,如下:
mViewPager.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {Log.e("MainActivity", "setOnTouchListener====");return mIsAnim;}});
效果如下:

好了,解决了快速滑动的问题。但是。。。。当滑动的瞬间,我们朝相反的方向滑动,就会停在两个pager中间的位置。其实这个问题,仔细想想也对,因为mIsAnim为true,所以我们拦截了ViewPager的事件,就造成了这个问题。
怎么解决呢?我想的是,我在根布局拦截事件,动画执行的过程中,根本不让事件下发到ViewPager,应该就能解决这个问题了。代码如下:
@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.e("MainActivity", "dispatchTouchEvent====");if(mIsAnim){return true;}return super.dispatchTouchEvent(ev);}
我重写了Activity的dispatchTouchEvent()方法,当动画过程中,拦截,否则走原有逻辑。
这样基本上没问题了,算是一个圆满的结局吧。
上面提到了,我们每次最多移动屏幕的一半,但是这个就有问题,比如我们的大图宽度是2000px,屏幕宽度是1080px,然后有5张page,我每次移动540,这样显示最后一张page的时候,大图不会有动画了,因为已经到头了。
所以需要动态算一下,每次可以移动的最大值,算法也很简单,如下:
mMoveWidth = (mLargeImageView.getImageWidth() - getScreenWidth(this)) / (mArrViews.size() - 1);
图片的宽度-屏幕的宽度,得到剩下的宽度,然后其他几个page平分即可。
本来下载了这里,但是写的比较多,所以专门另起了一篇文章,链接如下:
ViewPage动画PageTransformer分析
直接上代码,如下:
private class MyTransformer implements ViewPager.PageTransformer {@Overridepublic void transformPage(View page, float position) {if (position < -1) { // [-Infinity,-1)Log.e("MainActivity", "============" + position);} else if (position <= 1) { // [-1,1]float scrollXOffset = page.getWidth() * 1.5f;ViewGroup tViewGroup = (ViewGroup) page;View tView;for (int i = 0; i < tViewGroup.getChildCount(); i++) {tView = tViewGroup.getChildAt(i);if (tView == null || tView instanceof ImageView) {continue;}tView.setTranslationX(scrollXOffset * position);// scrollXOffset *= 0.5;}} else { // (1,+Infinity]Log.e("MainActivity", "============" + position);}}}
这里需要注意,我在之前的文章里面也说过
setTranslationX(0)会恢复TranslationX的值
所以我们利用这个特性,确保在动画结束的时候,setTranslationX()的参数传入的为0,这样布局就还原了。
横屏的时候,大图没有刷新,还是显示为竖屏的宽度,这个就需要我们自己刷新一次了,代码如下:
Activity监听onConfigurationChanged()
@Overridepublic void onConfigurationChanged(Configuration newConfig) {super.onConfigurationChanged(newConfig);mMoveWidth = (mLargeImageView.getImageWidth() - getScreenWidth(this)) / (mArrViews.size() - 1);if (mLargeImageView != null) {mLargeImageView.onConfigurationChanged(mCurrentPosition, mMoveWidth);}}
public void onConfigurationChanged(int pPosition, int pMoveWidth) {mIsFirstMeasure = true;mPosition = pPosition;mMoveWidth = pMoveWidth;}
因为有可能,我是在第二页才横屏的,所以需要传position参数。

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