【Android Developers Training】 70. 使用ViewPager实现屏幕滑动
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。
原文链接:http://developer.android.com/training/animation/screen-slide.html
屏幕滑动是从一个完整的屏幕转换到另一个屏幕的动画,对于配置向导或者幻灯片的应用场景是很常见的。这节课将会展示如何使用support library.ViewPager提供的
ViewPager来自动实现滑动的动画。下面是一个屏幕滑动的动画例子:
如果你希望略过这部分内容直接看代码样例,可以直接下载样例代码,然后选择淡入淡出动画的例子。下面的文件是实现代码:
src/ScreenSlidePageFragment.java
src/ScreenSlideActivity.java
layout/activity_screen_slide.xml
layout/fragment_screen_slide_page.xml
一). 创建视图
创建一个布局文件,你马上就会使用到它作为一个fragment的内容布局。下面的代码是一个例子,包含了一个文本框显示一些文本:
<!-- fragment_screen_slide_page.xml --> <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" /> </ScrollView>
同时记得给这个fragment定义一个字符串。
二). 创建一个Fragment
创建一个Fragment类,在onCreateView()中返回你刚才创建的布局。当你需要向用户显示新的一页时,你可以之后在父activity中创建这个fragment的实例:
import android.support.v4.app.Fragment; ... 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的布局:
<!-- activity_screen_slide.xml --> <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,它完成下列事情:
- 将视图的布局设置为包含了ViewPager的布局。
- 创建一个继承FragmentStatePagerAdapter抽象类的类,并且实现getItem()方法来提供ScreenSlidePageFragment的实例作为新的页面。这个页面适配器(Pager Adapter)还要求你实现getCount()方法,它返回适配器需要创建的页面数量(比如说有五页)。
- 将PagerAdapter与ViewPager关联起来。
- 处理设备的后退按键,让它实现在虚拟堆栈中向后移动。如果用户已经在第一页了,回到activity的后退堆栈。
import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; ... 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); // Instantiate a ViewPager and a PagerAdapter. mPager = (ViewPager) findViewById(R.id.pager); mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); 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接口,并将它提供给ViewPager。该接口有一个共有方法:transformPage()。每次屏幕切换,该方法都会为每一个可见的页面(一般来说都只有一个可见页面)及相邻的离开屏幕范围的页面执行一次。例如,如果当前第三页是可见的,并且用户滑动到了第四页,那么transformPage()方法会在手势执行的过程中,分别为第二页,第三页和第四页进行调用。
在你的transformPage()的实现中,你可以根据当前屏幕所显示的页的位置,确定哪些页需要变化,创建自定义的滑动动画,着可以通过transformPage()方法的position参数获得。
position变量指出了某个页面相对于中心的位置。这个变量会随着用户在页面间切换而动态变化。当一个页面充满屏幕时,它的position值是0。当一个页面被拉出到了屏幕右侧,它的position值是1。如果用户在页面一和页面二切换过程中,那么页面一的position值为-0.5,页面二的position值为0.5。根据页面对于屏幕的位置,你可以通过设置页面的属性,比如:setAlpha(),setTranslationX()或setScaleY(),来创建自定义的滑动动画。
当你有一个PageTransformer的实现,在你的实现中调用setPageTransformer()来应用你的自定义动画。例如,如果你有一个叫ZoomOutPageTransformer的PageTransformer,你可以这样设置你的自定义动画:
ViewPager mPager = (ViewPager) findViewById(R.id.pager); ... mPager.setPageTransformer(true, new ZoomOutPageTransformer());
拉远页面切换(Zoom-out)
这个动画会缩小页面然后在滑动过程中淡化相邻的页面。在一个页面接近中心的时候,它会回到它的标准大小并且淡入。
public class ZoomOutPageTransformer implements ViewPager.PageTransformer { private static final float MIN_SCALE = 0.85f; private static final 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); } } }
深度页面动画(Depth)
这个动画对于向左滑动的页面使用默认的滑动动画,而对于从右侧滑入的页面使用深度动画。该动画淡出页面,并且线性地缩小它的大小。
Note:
在深度动画过程中,默认的动画(屏幕滑动)仍然发生,所以你必须用一个负X方向来抵消屏幕滑动,例如
view.setTranslationX(-1 * view.getWidth() * position);
下面的例子展示了如何在一个工作页面中,抵消默认的屏幕滑动动画:
public class DepthPageTransformer implements ViewPager.PageTransformer { private static final 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); } } }