自定义ViewPager的切换动画ScalePageTransformer

本文介绍如何在Android中创建一个自定义ViewPager,实现ScalePageTransformer效果,使得页面在切换时具有平滑的缩放动画。通过设置android:clipToPadding为false,可以在同一屏幕显示多个页面。在实现过程中,通过分析ViewPager源码,修改PageTransformer中的position计算方式,解决了全屏缩放时页面不在屏幕中心的问题。提供了一个GitHub上的demo链接供参考。

废话不多说了,先看看效果
demo效果图
接下来,我们分析一下实现过程。
首先,使用ViewPager实现类似Gallery效果只需要在布局文件上加上android:clipToPadding=”true”就行。clipToPadding这个属 性指的是是否允许子组件布局到父组件的padding区域,默认是true,即不允许。将这个属性设置为false之后,就能实在在同一个屏幕显示多个PageView的效果。

       <android.support.v4.view.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:overScrollMode="never"
            android:layout_centerInParent="true"
            android:clipToPadding="false"
            android:paddingLeft="70dp"
            android:paddingRight="70dp">

        </android.support.v4.view.ViewPager>

其次是要实现page页的切换效果。要实现切换效果就要使用ViewPager的PageTransformer类。如果我们的page是横向满屏的话,直接继承该类就行。例如实现切换时的缩放效果,代码可以简单的这么写:

public class ScalePageTransformer implements ViewPager.PageTransformer {

    public static final float MIN_SCALE = 0.75f;

    @Override
    public void transformPage(View page, float position) {

        if (position < -1) { // [-Infinity,-1)
            page.setScaleX(MIN_SCALE);
            page.setScaleY(MIN_SCALE);

        } else if (position <= 1) { // [-1,1]
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            page.setScaleX(scaleFactor);
            page.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            page.setScaleX(MIN_SCALE);
            page.setScaleY(MIN_SCALE);

        }

    }

}

但是如果我们的想要在同一个屏幕显示多个page,那么,我们就会发现,同样使用上面的代码,当page放大到最大的时候,page的位置并不在屏幕的正中间,而是在屏幕的最左侧。
通过ViewPager的源代码,我们发现,问题就出在transformPage的pisition参数上,ViewPager相关源码如下:

if (mPageTransformer != null) {
            final int scrollX = getScrollX();
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = getChildAt(i);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                if (lp.isDecor) continue;
                final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
                mPageTransformer.transformPage(child, transformPos);
            }
}

private int getClientWidth() {
        return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}

我们发现position表示的是page页左侧到屏幕左侧的距离与ViewPager内容区域(除去padding区域)的比值。因此当position的值为0的时候,page页左侧正好与屏幕左侧重合,这显然不是我们想要的。那么我们就可以参考ViewPager的源码,改进position的计算方式,达到我们需要的效果。参考代码如下:

package demo.xuqingqi.cardviewpager;

import android.support.v4.view.ViewPager;
import android.view.View;

/**
 * Created on 2016/8/29.
 */
public class ScalePageTransformer implements ViewPager.PageTransformer {

    public static final float MIN_SCALE = 0.75f;

    /**
     * 重新计算 page 的位置transformPos,使得
     * 当page居中时 transformPos为0
     * 当page向左偏移时 transformPos为[-Infinity, 0)
     * 当page向右偏移时 transformPos为(0, +Infinity]
     */
    @Override
    public void transformPage(View page, float position) {

        ViewPager viewPager = (ViewPager) page.getParent();
        int scrollX = viewPager.getScrollX();
        int clientWidth = viewPager.getMeasuredWidth() -
                viewPager.getPaddingLeft() - viewPager.getPaddingRight();
        int offsetX = page.getLeft() - scrollX;
        int parentWidth = viewPager.getMeasuredWidth();
        int childWidth = page.getWidth();
        float deltaX = (float) (parentWidth - childWidth) / 2;
        float transformPos = (offsetX - deltaX) / clientWidth;

        if (transformPos < -1) { // [-Infinity,-1)
            page.setScaleX(MIN_SCALE);
            page.setScaleY(MIN_SCALE);

        } else if (transformPos <= 1) { // [-1,1]
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(transformPos));
            page.setScaleX(scaleFactor);
            page.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            page.setScaleX(MIN_SCALE);
            page.setScaleY(MIN_SCALE);

        }

    }

}

另附demo:github地址

<think>我们有一个需求:在Android中根据不同的类型(Type)动态切换ViewPager的页面切换动画。已知ViewPager2支持通过setPageTransformer来设置页面切换动画。我们可以根据类型来设置不同的PageTransformer。实现思路:1.定义一个枚举或常量来表示不同的动画类型(例如:无动画、淡入淡出、缩放、旋转等)。2.在设置ViewPager2时,根据当前类型选择对应的PageTransformer。3.在类型改变时,重新设置ViewPager2的PageTransformer。注意:ViewPager2的setPageTransformer方法可以设置一个PageTransformer对象,该对象实现了transformPage方法,用于自定义页面切换时的动画效果。步骤:a.定义不同的PageTransformer类,每个类实现一种动画效果。b.根据类型动态选择并设置PageTransformer。示例代码:首先,定义几种常见的PageTransformer:1.无动画:直接使用null或者一个不做任何变换的PageTransformer。2.淡入淡出:AlphaPageTransformer(改变透明度)3.缩放:ScalePageTransformer(改变缩放比例)4.深度效果:DepthPageTransformer(类似官方示例的深度效果)然后,我们创建一个方法,根据类型来设置动画:```javapublicvoidsetPagerAnimation(Typetype){switch(type){caseNONE:viewPager2.setPageTransformer(null);break;caseFADE:viewPager2.setPageTransformer(newAlphaPageTransformer());break;caseSCALE:viewPager2.setPageTransformer(newScalePageTransformer());break;caseDEPTH:viewPager2.setPageTransformer(newDepthPageTransformer());break;//其他类型...}}```下面给出几种PageTransformer的具体实现示例:1.AlphaPageTransformer(淡入淡出):```javapublicclassAlphaPageTransformerimplementsViewPager2.PageTransformer{privatestaticfinalfloatMIN_ALPHA=0.5f;//最小透明度publicvoidtransformPage(Viewpage,floatposition){if(position<-1){//[-Infinity,-1)page.setAlpha(MIN_ALPHA);}elseif(position<=1){//[-1,1]//计算透明度:从1->MIN_ALPHA,当页面在中间时透明度为1,在两侧时逐渐变暗floatalphaFactor=Math.max(MIN_ALPHA,1-Math.abs(position));page.setAlpha(alphaFactor);}else{//(1,+Infinity]page.setAlpha(MIN_ALPHA);}}}```2.ScalePageTransformer(缩放):```javapublicclassScalePageTransformerimplementsViewPager2.PageTransformer{privatestaticfinalfloatMIN_SCALE=0.8f;@OverridepublicvoidtransformPage(Viewpage,floatposition){if(position<-1){//[-Infinity,-1)page.setScaleX(MIN_SCALE);page.setScaleY(MIN_SCALE);}elseif(position<=1){//[-1,1]floatscaleFactor=Math.max(MIN_SCALE,1-Math.abs(position)*0.2f);page.setScaleX(scaleFactor);page.setScaleY(scaleFactor);}else{//(1,+Infinity]page.setScaleX(MIN_SCALE);page.setScaleY(MIN_SCALE);}}}```3.DepthPageTransformer(深度效果,类似官方示例):```javapublicclassDepthPageTransformerimplementsViewPager2.PageTransformer{privatestaticfinalfloatMIN_SCALE=0.75f;publicvoidtransformPage(Viewview,floatposition){intpageWidth=view.getWidth();if(position<-1){//[-Infinity,-1)view.setAlpha(0f);}elseif(position<=0){//[-1,0]view.setAlpha(1f);view.setTranslationX(0f);view.setScaleX(1f);view.setScaleY(1f);}elseif(position<=1){//(0,1]view.setAlpha(1-position);view.setTranslationX(pageWidth*-position);floatscaleFactor=MIN_SCALE+(1-MIN_SCALE)*(1-Math.abs(position));view.setScaleX(scaleFactor);view.setScaleY(scaleFactor);}else{//(1,+Infinity]view.setAlpha(0f);}}}```注意:在ViewPager2中,PageTransformer的transformPage方法中的position参数表示当前页面相对于屏幕中心的位置。-当前页面在中心时,position为0。-当前页面完全滑出屏幕左侧时,position为-1。-当前页面完全滑出屏幕右侧时,position为1。另外,当类型改变时,调用setPagerAnimation方法即可切换动画。补充:ViewPager2设置PageTransformer时,还可以设置一个boolean参数(reverseDrawingOrder)和一个PageTransformer,但是通常我们只需要使用一个参数的方法即可。参考引用[2]中提到了使用ViewPager2和TabLayout实现导航,其中也涉及到了页面切换事件。不过,我们这里主要关注动画切换。参考引用[3]中提到了阅读器翻页效果的实现,其中可能包含复杂的动态分页和章节切换,但动画切换的原理是类似的。因此,我们可以通过上述方式实现动态切换ViewPager2的页面切换动画。如果使用的是旧版的ViewPagerandroidx.viewpager.widget.ViewPager),同样有setPageTransformer方法,实现方式类似。但是注意,ViewPager2和ViewPagerPageTransformer接口略有不同(ViewPager2的PageTransformer在androidx.viewpager2.widget包下)。最后,记得在不需要的时候清除动画,可以设置null来移除动画。相关问题:1.如何为ViewPager2自定义更复杂的页面切换动画?2.ViewPager2的PageTransformer工作原理是什么?3.在动态切换动画时,如何保证动画的平滑过渡?</think>### 在Android中动态切换ViewPager页面切换动画的实现方式 在Android中为`ViewPager`或`ViewPager2`动态切换页面动画的核心是通过`setPageTransformer()`方法,结合条件判断实现不同动画效果的切换。以下是具体实现步骤: --- #### **1. 定义动画类型枚举** ```java public enum AnimationType { NONE, // 无动画 FADE, // 淡入淡出 ZOOM, // 缩放效果 DEPTH, // 深度效果 CUSTOM // 自定义动画 } ``` --- #### **2. 实现不同动画PageTransformer** 根据动画类型创建对应的`PageTransformer`实现类: ##### **(1) 淡入淡出动画** ```java public class FadeTransformer implements ViewPager2.PageTransformer { @Override public void transformPage(@NonNull View page, float position) { if (position < -1 || position > 1) { page.setAlpha(0.3f); } else { float alpha = 1 - Math.abs(position); page.setAlpha(Math.max(0.3f, alpha)); } } } ``` ##### **(2) 缩放动画** ```java public class ZoomTransformer implements ViewPager2.PageTransformer { private static final float MIN_SCALE = 0.85f; @Override public void transformPage(@NonNull View page, float position) { if (position < -1 || position > 1) { page.setScaleX(MIN_SCALE); page.setScaleY(MIN_SCALE); } else { float scale = Math.max(MIN_SCALE, 1 - Math.abs(position) * 0.15f); page.setScaleX(scale); page.setScaleY(scale); } } } ``` --- #### **3. 动态切换动画的核心方法** ```java // 根据类型动态设置动画 public void setPagerAnimation(AnimationType type) { ViewPager2 viewPager = findViewById(R.id.view_pager); switch (type) { case NONE: viewPager.setPageTransformer(null); // 清除动画 break; case FADE: viewPager.setPageTransformer(new FadeTransformer()); break; case ZOOM: viewPager.setPageTransformer(new ZoomTransformer()); break; case DEPTH: // 使用AndroidX内置的深度动画 viewPager.setPageTransformer(new MarginPageTransformer(50)); break; case CUSTOM: viewPager.setPageTransformer(new YourCustomTransformer()); break; } } ``` --- #### **4. 使用示例** 在需要切换动画的地方调用: ```java // 切换到淡入淡出动画 setPagerAnimation(AnimationType.FADE); // 切换到无动画 setPagerAnimation(AnimationType.NONE); ``` --- #### **5. 注意事项** 1. **兼容性**: - `ViewPager2`(推荐)直接支持`setPageTransformer` - 传统`ViewPager`需调用`setPageTransformer(true, transformer)`[^1] 2. **性能优化**: - 避免在`transformPage()`中执行耗时操作 - 复杂动画建议使用`Property Animation` API 3. **动画冲突处理**: - 调用`setPageTransformer(null)`可清除旧动画 - 与`TabLayout`联动时需同步更新指示器[^2] --- #### **6. 扩展:阅读器场景的章节切换** 在阅读器应用中,可通过监听页面位置实现章节切换动画: ```java viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { // 根据章节类型切换动画 if (isTextChapter(position)) { setPagerAnimation(AnimationType.FADE); } else if (isImageChapter(position)) { setPagerAnimation(AnimationType.ZOOM); } } }); ``` 这种动态切换策略常用于区分文字/图片内容的过渡效果[^3]。 --- ### 相关问题 1. **如何为ViewPager2实现3D翻转动画效果?** 需结合`Camera`和`Matrix`自定义`PageTransformer`,通过旋转角度计算实现立体效果。 2. **页面切换动画导致卡顿如何优化?** 建议:(1) 简化动画计算逻辑 (2) 使用硬件加速 (3) 避免在`transformPage`中创建对象[^1]。 3. **ViewPager2与Fragment联用时动画失效怎么办?** 检查`FragmentStateAdapter`的页面复用机制,确保`transformPage`中正确处理`Fragment`的根视图。 4. **如何实现类似阅读器的双向翻页动画(左右滑动方向不同)?** 需在`PageTransformer`中根据`position`正负值区分滑动方向,应用不同的动画策略[^3]。 [^1]: ViewPager的`setPageTransformer`方法需启用反向绘制顺序以兼容旧版本 [^2]: 当ViewPager与TabLayout联动时,动画切换需考虑指示器同步 [^3]: 阅读器应用通常需要根据章节类型动态切换动画策略
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值