PageTransformer

本文详细介绍了如何在Android应用中使用ViewPager组件实现屏幕间的流畅切换,包括创建视图、布局文件、Fragment类、适配器及页面转换动画的定制。通过实例演示,用户可以快速掌握创建分步引导页面、实现页面间平滑过渡和自定义动画的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于屏幕切换使用ViewPager

创建视图

创建一个布局文件,之后你将把它用作fragment的内容。
?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
<com.example.android.animationsdemo.ScrollView
    xmlns:android= "http://schemas.android.com/apk/res/android"
    android:id= "@+id/content"
    android:layout_width= "match_parent"
    android:layout_height= "match_parent" >
  
         <TextView style= "?android:textAppearanceMedium"
             android:padding= "16dp"
             android:lineSpacingMultiplier= "1.2"
             android:layout_width= "match_parent"
             android:layout_height= "wrap_content"
             android:text= "@string/lorem_ipsum" />
  
</com.example.android.animationsdemo.ScrollView>


创建Fragment

创建一个Fragment类,在onCreateView()中返回刚才创建的布局。当你需要一个新的页面
来展示给用户时,你可以创建一个该fragment的实例。
?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
public class ScreenSlidePageFragment extends Fragment {
  
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         ViewGroup rootView = (ViewGroup) inflater.inflate(
                R.layout.fragment_screen_slide_page, container, false );
  
         return rootView;
    }
}


添加一个ViewPager

ViewPager在页面切换的时候设有内置的滑动手势,它能够默认展示屏幕动画。ViewPager使
用PagerAdapter显示新的页面,所以PagerAdapter将会使用你之前创建的fragment类。
开始时,先创建一个包含ViewPager的布局文件:
?
代码片段,双击复制
01
02
03
04
05
<android.support.v4.view.ViewPager
      xmlns:android= "http://schemas.android.com/apk/res/android"
      android:id= "@+id/pager"
      android:layout_width= "match_parent"
      android:layout_height= "match_parent" />

创建一个activity做下面的事情:
1.将有ViewPager的布局设置为content view。
2.创建一个继承于FragmentStatePagerAdapter抽象类的子类,实现getItem()方法提供一个
ScreenSlidePageFragment的实例作为新的页面。该adapter也需要你实现getCount()方法,
它会返回在adapter中创建的页面的数量。
3.将PagerAdapter配置到ViewPager。
4.重写设备的back键,使viewpager退回到前一个fragment。如果用户已经在第一个页面,则
返回前一个activity。
?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class ScreenSlidePagerActivity extends FragmentActivity {
    /**
      * The number of pages (wizard steps) to show in this demo.
      */
    private static final int NUM_PAGES = 5 ;
  
    /**
      * The pager widget, which handles animation and allows swiping horizontally to access previous
      * and next wizard steps.
      */
    private ViewPager mPager;
  
    /**
      * The pager adapter, which provides the pages to the view pager widget.
      */
    private PagerAdapter mPagerAdapter;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_screen_slide_pager);
  
         // Instantiate a ViewPager and a PagerAdapter.
         mPager = (ViewPager) findViewById(R.id.pager);
         mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager());
         mPager.setAdapter(mPagerAdapter);
    }
  
    @Override
    public void onBackPressed() {
         if (mPager.getCurrentItem() == 0 ) {
             // If the user is currently looking at the first step, allow the system to handle the
             // Back button. This calls finish() on this activity and pops the back stack.
             super .onBackPressed();
         } else {
             // Otherwise, select the previous step.
             mPager.setCurrentItem(mPager.getCurrentItem() - 1 );
         }
    }
  
    /**
      * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
      * sequence.
      */
    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
         public ScreenSlidePagerAdapter(FragmentManager fm) {
             super (fm);
         }
  
         @Override
         public Fragment getItem( int position) {
             return new ScreenSlidePageFragment();
         }
  
         @Override
         public int getCount() {
             return NUM_PAGES;
         }
    }
}


使用PageTransformer定制动画

要显示一个不同于默认动动画的动画,实现ViewPager.PageTransformer接口,并将其提供给
view pager。该接口提供了唯一一个方法transformPage()。在每一个屏幕变换的点,该方
法都被每一个可见的页面和每一个相邻退出屏幕的页面调用。例如,如果第三个页面是可见
的,用户拖动屏幕滑向第四个界面,transformPage()被两个页面调用,第三个和第四个页
面,在用户手势的每一步。

在你的transformPage()的实现中,你可以通过基于当前页面的位置确定哪个页面将要被显
示,来创建定制的动画,当前页面的位置可由transformPage()的参数确定。

该位置参数明确了相对屏幕中心位置的页面。这是一个动态变化的参数,随着用户滑动屏幕
而变化。当一个页面充满整个屏幕时,它的位置是0.当一个页面从屏幕的右侧退出时,其位
置为1.如果用户滑动到第一页和第二页的中间时,第一页的位置为-0.5,第二页的位置为0.5。
基于在屏幕上页面的位置,你可以通过使用setAlpha ( ) , setTranslationX ( ) ,或
setScaleY ()方法设置页面属性,来创建自定义动画。

当你有一个PageTransformer的实现时,调用setPageTransformer()方法来提供自定义的动
画。
?
代码片段,双击复制
01
02
03
ViewPager pager = (ViewPager) findViewById(R.id.pager);
  ...
  pager.setPageTransformer( true , new ZoomOutPageTransformer());


Zoom-out页面变换

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
    private static float MIN_SCALE = 0 .85f;
    private static float MIN_ALPHA = 0 .5f;
  
    public void transformPage(View view, float position) {
         int pageWidth = view.getWidth();
         int pageHeight = view.getHeight();
  
         if (position < - 1 ) { // [-Infinity,-1)
             // This page is way off-screen to the left.
             view.setAlpha( 0 );
  
         } else if (position <= 1 ) { // [-1,1]
             // Modify the default slide transition to shrink the page as well
             float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
             float vertMargin = pageHeight * ( 1 - scaleFactor) / 2 ;
             float horzMargin = pageWidth * ( 1 - scaleFactor) / 2 ;
             if (position < 0 ) {
                view.setTranslationX(horzMargin - vertMargin / 2 );
             } else {
                view.setTranslationX(-horzMargin + vertMargin / 2 );
             }
  
             // Scale the page down (between MIN_SCALE and 1)
             view.setScaleX(scaleFactor);
             view.setScaleY(scaleFactor);
  
             // Fade the page relative to its size.
             view.setAlpha(MIN_ALPHA +
                     (scaleFactor - MIN_SCALE) /
                     ( 1 - MIN_SCALE) * ( 1 - MIN_ALPHA));
  
         } else { // (1,+Infinity]
             // This page is way off-screen to the right.
             view.setAlpha( 0 );
         }
    }
}


深度页面变换

?
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static float MIN_SCALE = 0 .75f;
  
    public void transformPage(View view, float position) {
         int pageWidth = view.getWidth();
  
         if (position < - 1 ) { // [-Infinity,-1)
             // This page is way off-screen to the left.
             view.setAlpha( 0 );
  
         } else if (position <= 0 ) { // [-1,0]
             // Use the default slide transition when moving to the left page
             view.setAlpha( 1 );
             view.setTranslationX( 0 );
             view.setScaleX( 1 );
             view.setScaleY( 1 );
  
         } else if (position <= 1 ) { // (0,1]
             // Fade the page out.
             view.setAlpha( 1 - position);
  
             // Counteract the default slide transition
             view.setTranslationX(pageWidth * -position);
  
             // Scale the page down (between MIN_SCALE and 1)
             float scaleFactor = MIN_SCALE
                     + ( 1 - MIN_SCALE) * ( 1 - Math.abs(position));
             view.setScaleX(scaleFactor);
             view.setScaleY(scaleFactor);
  
         } else { // (1,+Infinity]
             // This page is way off-screen to the right.
             view.setAlpha( 0 );
         }
    }
}
一直都很喜欢Instagram的快拍(Story)功能,也很喜欢他们的翻转效果,是一种简单的3D翻转效果。大致效果如下:instagramstory.gif貌似最近微博也出了一个差不多的Story的功能,用的翻转动画也是和Instagram一样。思路看到这样的效果,很容易想到用ViewPager的Transformer动画来实现。当然这种翻转效果网上也有相应的实现,就是以View的左边或右边为旋转轴进行空间上Y轴的旋转。于是很容易我们可以写出下面的代码public class StereoPagerTransformer implements ViewPager.PageTransformer {   private static final float MAX_ROTATE_Y = 90;   private final float pageWidth;   public StereoPagerTransformer(float pageWidth) {     this.pageWidth = pageWidth;   }   public void transformPage(View view, float position) {     view.setPivotY(view.getHeight() / 2);     if (position < -1) { // [-Infinity,-1)       // This page is way off-screen to the left.       view.setPivotX(0);       view.setRotationY(90);     } else if (position <= 0) { // [-1,0]       view.setPivotX(pageWidth);       view.setRotationY(position * MAX_ROTATE_Y);     } else if (position <= 1) { // (0,1]       view.setPivotX(0);       view.setRotationY(position * MAX_ROTATE_Y);     } else { // (1, Infinity]       // This page is way off-screen to the right.       view.setPivotX(0);       view.setRotationY(90);     }   } }然后运行代码看一下我们实现的效果:badtransformer.gif额,总觉哪里不对,嗯,就是动画的速度不对,上面的写法就是一个匀速进行的动画,效果非常不理想,这时我们就要重新写一个插值器(TimeInterpolator ),在尝试了系统自带的差值器后,发现效果仍然不是很理想。于是决定自己写一个插值器。关于插值器根据TimeInterpolator代码中的文档可以得知,插值器用于控制动画进行的速度,其输入值为0~1,输出值也为0~1。/**  * A time interpolator defines the rate of change of an animation. This allows animations  * to have non-linear motion, such as acceleration and deceleration.  */ public interface TimeInterpolator {     /**      * Maps a value representing the elapsed fraction of an animation to a value that represents      * the interpolated fraction. This interpolated value is then multiplied by the change in      * value of an animation to derive the animated value at the current elapsed animation time.      *      * @param input A value between 0 and 1.0 indicating our current point      *        in the animation where 0 represents the start and 1.0 represents      *        the end      * @return The interpolation value. This value can be more than 1.0 for      *         interpolators which overshoot their targets, or less than 0 for      *         interpolators that undershoot their targets.      */     float getInterpolation(float input); }经过简单的分析,这次我们的动画应该在动画前半段时进行缓慢一些(也就是输入值在0到某个值之间),在后半段时(也就是输入值在某个值到1之间)进行的快速一些。经过简单的调整,最终我写了如下的插值器private static final TimeInterpolator sInterpolator = new TimeInterpolator() {         @Override         public float getInterpolation(float input) {             if (input < 0.7) {                 return input * (float) pow(0.7, 3) * MAX_ROTATE_Y;             } else {                 return (float) pow(input, 4) * MAX_ROTATE_Y;             }         }     };再次运行代码,这次效果看上去好多了,哈哈,一个简单又好看的效果就完成了。goodtransformer.gif最后附上完整的代码:import android.animation.TimeInterpolator; import android.support.v4.view.ViewPager; import android.view.View; import static java.lang.Math.pow; /**  * @author wupanjie  */ public class StereoPagerTransformer implements ViewPager.PageTransformer {     private static final float MAX_ROTATE_Y = 90;     private static final TimeInterpolator sInterpolator = new TimeInterpolator() {         @Override         public float getInterpolation(float input) {             if (input < 0.7) {                 return input * (float) pow(0.7, 3) * MAX_ROTATE_Y;             } else {                 return (float) pow(input, 4) * MAX_ROTATE_Y;             }         }     };     private final float pageWidth;     public StereoPagerTransformer(float pageWidth) {         this.pageWidth = pageWidth;     }     public void transformPage(View view, float position) {         view.setPivotY(view.getHeight() / 2);         if (position < -1) { // [-Infinity,-1)             // This page is way off-screen to the left.             view.setPivotX(0);             view.setRotationY(90);         } else if (position <= 0) { // [-1,0]             view.setPivotX(pageWidth);             view.setRotationY(-sInterpolator.getInterpolation(-position));         } else if (position <= 1) { // (0,1]             view.setPivotX(0);             view.setRotationY(sInterpolator.getInterpolation(position));         } else { // (1, Infinity]             // This page is way off-screen to the right.             view.setPivotX(0);             view.setRotationY(90);         }     } }总结动画的灵魂在于它的插值器,插值器控制了动画进行的速度,这次我选择自己写了一个插值器作为练手,其实我这次写的插值器效果仍不是很平滑,动画的插值器也应该用贝塞尔曲线来制作,这样我们的动画就会进行的更平滑。具体大家可以参考自带的PathInterpolator,是在API 21以后引入的,当然它也有对应的兼容包。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值