Android - ViewPager进阶篇之转场特效

一.使用最简单的方式创建Android应用引导页(效果如下)

my_view_pager.gif

该引导仅需要一个ViewPager和一个ViewPager适配器即可完成。

接下来,小编将从零开始讲解该引导如何实现,准备好了没O(∩_∩)O~,小司机要开车了...

1.首先,为我们的GuideActivity创建一个布局文件(非常简单的布局)。



    


2.接下来,完成GuideActivity(引导活动页面)。

public class GuideActivity extends AppCompatActivity {

    private ViewPager mViewPager;
    private ViewPagerAdapter mViewPagerAdapter; // ViewPager适配器
    private int[] images; // 图片资源引用数组(填充引用图片的Id)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initToolbar(); // 初始化标题栏
        initView(); // 初始化视图
        initData(); // 初始化数据
    }

    /**
     * 初始化标题栏
     */
    private void initToolbar() {
        getSupportActionBar().hide(); // 隐藏标题栏
    }

    /**
     * 初始化视图
     */
    private void initView() {
        mViewPager = (ViewPager) findViewById(R.id.viewpager);
    }

    /**
     * 初始化数据
     */
    private void initData() {
        images = new int[]{R.mipmap.guide_page_one, R.mipmap.guide_page_two, R.mipmap.guide_page_three, R.mipmap.guide_page_four};
        mViewPagerAdapter = new ViewPagerAdapter(GuideActivity.this, images);
        mViewPager.setAdapter(mViewPagerAdapter);
    }
}

图片资源需要各位自己准备,小编就不提供了...O(∩_∩)O~

我们的GuideActivity只是完成了一部分,因为ViewPagerAdapter我们还没有构建,别急...接下来就是它了...

3.最后,构建ViewPager的适配器ViewPagerAdapter。

/**
 * @ClassName: ViewPagerAdapter
 * @Description: ViewPager适配器
 * @Author Wangnan
 * @Date 2016/8/25
 */
public class ViewPagerAdapter extends PagerAdapter {

    private Context mContext;
    private int[] images; // 图片资源引用数组

    public ViewPagerAdapter(Context context, int[] datas){
        images = datas;
        mContext = context;
    }

    /**
     * 获取数据(图片)数量
     * @return
     */
    @Override
    public int getCount() {
        return images.length;
    }

    /**
     * 当前View是否是instantiateItem中返回的对象
     * @param view 当前view
     * @param object 由instantiateItem()方法返回的对象
     * @return
     */
    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    /**
     * 初始化Item并向页面添加
     * @param container ViewPager
     * @param position 要实例化item对应的页面位置
     * @return
     */
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        ImageView mImageView = createImageView(mContext, position);
        container.addView(mImageView);
        return mImageView;
    }

    /**
     * 移除ViewPager中的Item
     * @param container viewpager
     * @param position 要移除页面的位置
     * @param object 要移除页面的对象
     */
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View)object);
    }

    /**
     * 创建ImageView
     * @param mContext
     * @param position
     * @return imageView
     */
    private ImageView createImageView(Context mContext, int position) {
        ImageView imageview = new ImageView(mContext);
        ViewPager.LayoutParams layoutParams = new ViewPager.LayoutParams(); // 等价于ViewGroup.LayoutParams(FILL_PARENT, FILL_PARENT)
        imageview.setLayoutParams(layoutParams); // 设置ImageView充满父容器
        imageview.setImageResource(images[position]); // 设置ImageView的显示图片
        imageview.setScaleType(ImageView.ScaleType.CENTER_CROP); // 设置ImageView缩放样式
        return imageview;
    }
}

这段代码可能对于初学者来讲很难理解,但小编已经做了很详细的解释。另外,小编仅教给大家如何使用,如果大家想要理解这些方法的调用原理,可以去查阅PagerAdapter的相关源码...

二.使用PageTransformer打造炫酷翻转动画

1.首先来看下Google为我们提供的样例DepthPageTransformer(下图为运行效果)

gif_my_viewpage1.gif

是不是感觉很神奇,接下来我们就看看如何使用PageTransformer实现这种效果...

(1).创建DepthPageTransformer(深度效果页面转换器)

/**
 * @ClassName: DepthPageTransformer
 * @Description: 深度效果页面转换器
 * @Author Wangnan
 * @Date 2016/8/25
 */
public class DepthPageTransformer implements ViewPager.PageTransformer{

    private static final float MIN_SCALE = 0.75F; // 最小缩放比例
    @Override
    public void transformPage(View page, float position) {
        if(position < -1){ // [负无穷,-1):当前页面已经滑出左边屏幕,我们已经看不到了
            page.setAlpha(0F);
        } else if (position <= 0){ // [-1, 0]:当前页面向左画出,已远离中心位置,但还未滑出左屏幕
            page.setAlpha(1F);
            page.setTranslationX(0F);
            page.setScaleX(1F);
            page.setScaleY(1F);
        } else if (position <= 1){ // (0,1]:下一页面已经进入屏幕,但还在进入并未到达中间位置
            page.setAlpha(1 - position);
            page.setTranslationX(page.getWidth() * -position);
            float scale = MIN_SCALE + (1 - MIN_SCALE) * (1 - position);
            page.setScaleX(scale);
            page.setScaleY(scale);
        } else { // (1, 正无穷]:下一页面还未进入屏幕
            page.setAlpha(0F);
        }
    }
}

小编对Google提供的样例进行了简单的说明...里面涉及到一些View的样式设置和偏移计算,可能新手朋友们有些难以理解,这里小编进行下详细的说明...老司机们可略过...O(∩_∩)O~

  • 当新页面还未进入屏幕的之前,我们使用page.setAlpha(0F),使新页面View保持透明...

  • 当新页面正在进入屏幕时:

  • page.setAlpha(1 - position),使新页面逐渐变得可见;

  • page.setTranslationX(page.getWidth() * -position),使新页面的X轴的偏移量从负的屏幕宽度变为0(即View从左向右平移),因为新页面又是从右向左滑入的,这两种滑动效果在视觉上抵消了...我们会感觉到页面没有出现过平移;

  • page.setScaleX(scale)和page.setScaleY(scale)设置X轴方向和Y轴方向的页面缩放,从新页面进入的角度看页面是从原页面大小的0.75倍增加到1倍...scale的范围是[0.75,1],scale的计算就不再细说了...

  • 当旧页面向左滑出还未出边界时:

  • page.setAlpha(1F); 保持透明度完全可见

  • page.setTranslationX(0F); 保持X轴的偏移量为0

  • page.setScaleX(1F); 保持X轴方向缩放为视图原大小

  • page.setScaleY(1F);保持Y轴方向缩放为视图原大小

  • 当旧页面向左滑出左边界时,我们已经看不到该视图了,page.setAlpha(0F),使滑出去的旧页面View保持透明...

(2).说了这么多,如何使用DepthPageTransformer呢?

别担心!仅需一行代码...我们在刚写完的GuideActivty的initData方法最后添加一行代码(如下图所示):

mViewPager.setPageTransformer(true, new DepthPageTransformer());

public class GuideActivity extends AppCompatActivity {

    ......

    /**
     * 初始化数据
     */
    private void initData() {
        images = new int[]{R.mipmap.guide_page_one, R.mipmap.guide_page_two, R.mipmap.guide_page_three, R.mipmap.guide_page_four};
        mViewPagerAdapter = new ViewPagerAdapter(GuideActivity.this, images);
        mViewPager.setAdapter(mViewPagerAdapter);
        mViewPager.setPageTransformer(true, new DepthPageTransformer());
    }
}

这样,我们的代码就完成了,上图的效果就出来了...

接下来我们简单介绍一下ViewPager的setPageTransformer方法。

setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer)

方法作用:允许应用程序为每一个页面自定义属性转换,重写默认的滑动外观。(注:API>=11才可使用)

第2个参数(transformer):传入自定义PageTransformer,修改每一个页面的动画属性。

第1个参数(reverseDrawingOrder):是否反转绘制顺序。

false(正常顺序 - first to last)从第一页开始绘制至最后一页

true(反转顺序 - last to first)从最后一页开始绘制至第一页

是不是好奇我们传true或false有何区别?

如果传入false,即mViewPager.setPageTransformer(false, new DepthPageTransformer());后面页面是后绘制的,所以后页面的View悬浮在前一页面的View之上,直白点讲,前面的视图无法遮住后面的视图,滑动效果如下图所示:

gif_my_viewpager2.gif

如果传入true,即mViewPager.setPageTransformer(true, new DepthPageTransformer());前面页面是后绘制的,所以前面页面的View悬浮在后一页面的View之上,直白点讲,前面的视图可以遮住后面的视图,效果如下图所示

gif_my_viewpage1.gif

2.自定义自己的PageTransformer

我们仿照Google提供的样例,写一个旋转的视图切换效果,这次先上代码,再给大家看效果。

1.定义旋转页面转换器RotatePageTransformer

/**
 * @ClassName: RotatePageTransformer
 * @Description: 旋转页面转换器
 * @Author Wangnan
 * @Date 2016/8/25
 */
public class RotatePageTransformer implements ViewPager.PageTransformer {

    private float maxRotate = 90f; // 最大旋转角度
    @Override
    public void transformPage(View page, float position) {
        if (position < -1){
            page.setPivotX(page.getWidth());
            page.setPivotY(page.getHeight() / 2);
            page.setRotationY(-maxRotate);
        }else if(position < 0){
            page.setPivotX(page.getWidth() * (0.5f + 0.5f * (- position))); // X轴支点坐标变化范围[page.getWidth()/2, page.getWidth()]
            page.setPivotY(page.getHeight() / 2);
            page.setRotationY(maxRotate * position);
        }else if(position <= 1){
            page.setPivotX(page.getWidth() * 0.5f * (1 - position)); // // X轴支点坐标变化范围[0, page.getWidth()/2]
            page.setPivotY(page.getHeight() / 2);
            page.setRotationY(maxRotate * position);
        }else{
            page.setPivotX(0);
            page.setPivotY(page.getHeight() / 2);
            page.setRotationY(maxRotate);
        }
    }
}

2.在GuideActivity中使用RotatePageTransformer。

public class GuideActivity extends AppCompatActivity {

    ......

    /**
     * 初始化数据
     */
    private void initData() {
        images = new int[]{R.mipmap.guide_page_one, R.mipmap.guide_page_two, R.mipmap.guide_page_three, R.mipmap.guide_page_four};
        mViewPagerAdapter = new ViewPagerAdapter(GuideActivity.this, images);
        mViewPager.setAdapter(mViewPagerAdapter);
        mViewPager.setPageTransformer(false, new RotatePageTransformer());
    }
}

然后我们看下视图效果:

gif_my_viewpager3.gif

最后解释下RotatePageTransformer中出现的3个方法:

  • page.setPivotX(float pivotX); 设置View的X轴支点,影响视图的旋转和缩放效果。
  • page.setPivotY(float pivotY); 设置View的Y轴支点,影响视图的旋转和缩放效果。
  • page.setRotationY(float rotationY); 设置View绕Y轴旋转的角度。

支点是一个很神奇的属性,大家可以尝试改变它的值看下新的视图效果。

我们的设置Y轴支点始终保持为当前View高度的一半:page.setPivotY(page.getHeight() / 2);

现在,我们改变下Y轴支点的值,使Y轴支点始终保持为当前View的高度:page.setPivotY(page.getHeight()),运行后再来看下效果:

gif_my_viewpager4.gif

看来这种场景下,旋转效果没有之前的好看,但如果是水平方向的矩形图片,效果就绚丽多了...

还有一些其他效果,简单展示一下,代码实现相对简单就不再贴源码了...

折叠效果(前一页的滑动速度是后一页的两倍)

gif_my_viewpage5.gif

限于篇幅原因,ViewPager的简单使用我们就写到这了,后续小编还准备出几篇ViewPager的进阶使用,请各位敬请期待。

你可能感兴趣的:(Android - ViewPager进阶篇之转场特效)