Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果

本文详细介绍如何在Android应用中实现京东商城首页滑动广告Banner的立体动画效果,并提供了多种预设动画效果供选择。

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

如有转载,请声明出处: 时之沙: http://blog.youkuaiyun.com/t12x3456


某天看到京东商城首页的滑动广告的Banner,在流动切换的时候有立体的动画效果,感觉很有意思,然后研究了下如何实现. 

废话不多说,接下来我会讲述如何实现这种效果,以及如何根据需求自定义出新的动画效果进行扩展实现.


首先还是看一下京东商城上的效果:

                                                 


像一般做这种效果怎么办呢?我的建议还是先在github或者google code上搜索开源库. 一来开源库一般做得比较成熟,API封装得较好,耦合性比较低. 二来项目比较利于维护.

(并不是说全自己实现的就不好,毕竟每个人实现的思路并不一样,相对于开源库来说,阅读别人的历史代码就相对比较麻烦,有bug或者有新需求的话,会很影响开发的效率)

下面还是直接上项目, 如上所示的效果已经有开源库的实现,而且还有很多其他动画补间效果:


JazzViewPager简介:

github地址: https://github.com/jfeinstein10/JazzyViewPager

该项目是基于ViewPager的一个重写,让我们看一下自带的Demo项目结构:



 这里我们可以看到,ViewPager的动画效果由nineoldandroids这个开源项目实现:

github地址:

  https://github.com/JakeWharton/NineOldAndroids

该动画库将Android3.0以上版本API实现的动画做了重写,可以兼容到3.0以下的版本


JazzyViewPager的集成:

接下来我们看一下如何将该开源库集成到自己的项目中:

1.布局文件中遵照自定义控件的写法即可:

[java]  view plain copy
  1. <com.jfeinstein.jazzyviewpager.JazzyViewPager  
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     android:id="@+id/jazzy_pager"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent" />  

2.设置ViewPager的动画效果:

这里首先介绍一下目前已经封装好的效果:

JazzyViewPager中的的枚举类:

[java]  view plain copy
  1. public enum TransitionEffect {  
  2.         Standard,  
  3.         Tablet,  
  4.         CubeIn,  
  5.         CubeOut,  
  6.         FlipVertical,  
  7.         FlipHorizontal,  
  8.         Stack,  
  9.         ZoomIn,  
  10.         ZoomOut,  
  11.         RotateUp,  
  12.         RotateDown,  
  13.         Accordion  
  14.     }  

怎么设置呢?非常简单:

[java]  view plain copy
  1. private JazzyViewPager mJazzy;  
  2. /* ... */  
  3. mJazzy.setTransitionEffect(TransitionEffect.*);  

在京东商城使用的效果即为TransitionEffect.CubeOut


这里我们还可以看一下其他的效果

TransitionEffect.Tablet

                                   


TransitionEffect.Stack

                                   

其他效果大家可以自己尝试下.


3.集成该开源库需要注意一些事项:

当ViewPager中的子View超过三个的时候,我们需要对PagerAdapter修改,重写InstantiateItem()方法,,会导致补间动画不能正常显示.

EX:

[java]  view plain copy
  1. private JazzyViewPager mJazzy;  
  2. /* ... */  
  3. @Override  
  4. public Object instantiateItem(ViewGroup container, final int position) {  
  5.     Object obj = super.instantiateItem(container, position);  
  6.     mJazzy.setObjectForPosition(obj, position);  
  7.     return obj;  
  8. }  

JazzyViewPager的修改:

如果大家还是嫌目前已经封装的效果还是不满意怎么办?项目有其他动画实现的需求怎么办?这里顺便讲下如何扩展该开源库:(红色部分为需要添加修改的代码)

1.在枚举类中添加所需的动画效果,这里以Test代替.

[java]  view plain copy
  1. public enum TransitionEffect {  
  2.         Standard,  
  3.         Tablet,  
  4.         CubeIn,  
  5.         CubeOut,  
  6.         FlipVertical,  
  7.         FlipHorizontal,  
  8.         Stack,  
  9.         ZoomIn,  
  10.         ZoomOut,  
  11.         RotateUp,  
  12.         RotateDown,  
  13.         Accordion,  
  14.         <span style="color:#FF0000;">Test</span>  
  15.  }  

2.增加动画效果的具体实现:

[java]  view plain copy
  1. protected void animateTest(View left, View right, float positionOffset) {     
  2.  if (mState != State.IDLE) {  
  3.   if (left != null) {  
  4.    //此处增加具体动画  
  5.  }  
  6.   if (right != null) {  
  7.    //此处增加具体动画实现   
  8.  }  
  9.  }  
  10. }  


3.在onPageScrolled的方法中,增加对应效果的处理:

[java]  view plain copy
  1. @Override  
  2. public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  
  3.   if (mState == State.IDLE && positionOffset > 0) {  
  4.   oldPage = getCurrentItem();  
  5.   mState = position == oldPage ? State.GOING_RIGHT : State.GOING_LEFT;  
  6. }  
  7. boolean goingRight = position == oldPage;     
  8. if (mState == State.GOING_RIGHT && !goingRight)  
  9.   mState = State.GOING_LEFT;  
  10.   else if (mState == State.GOING_LEFT && goingRight)  
  11.   mState = State.GOING_RIGHT;  
  12.   
  13.   float effectOffset = isSmall(positionOffset) ? 0 : positionOffset;  
  14.   
  15.   // mLeft = getChildAt(position);  
  16.   // mRight = getChildAt(position+1);  
  17.   mLeft = findViewFromObject(position);  
  18.   mRight = findViewFromObject(position+1);  
  19.   
  20.   if (mFadeEnabled)  
  21.   animateFade(mLeft, mRight, effectOffset);  
  22.   if (mOutlineEnabled)  
  23.   animateOutline(mLeft, mRight);  
  24.   
  25.   switch (mEffect) {  
  26.   case Standard:  
  27.   break;  
  28.   case Tablet:  
  29.   animateTablet(mLeft, mRight, effectOffset);  
  30.   break;  
  31.   case CubeIn:  
  32.   animateCube(mLeft, mRight, effectOffset, true);  
  33.   break;  
  34.   case CubeOut:  
  35.   animateCube(mLeft, mRight, effectOffset, false);  
  36.   break;  
  37.   case FlipVertical:  
  38.   animateFlipVertical(mLeft, mRight, positionOffset, positionOffsetPixels);  
  39.   break;  
  40.   case FlipHorizontal:  
  41.   animateFlipHorizontal(mLeft, mRight, effectOffset, positionOffsetPixels);  
  42.   case Stack:  
  43.   animateStack(mLeft, mRight, effectOffset, positionOffsetPixels);  
  44.   break;  
  45.   case ZoomIn:  
  46.   animateZoom(mLeft, mRight, effectOffset, true);  
  47.   break;  
  48.   case ZoomOut:  
  49.   animateZoom(mLeft, mRight, effectOffset, false);  
  50.   break;  
  51.   case RotateUp:  
  52.   animateRotate(mLeft, mRight, effectOffset, true);  
  53.   break;  
  54.   case RotateDown:  
  55.   animateRotate(mLeft, mRight, effectOffset, false);  
  56.   break;  
  57.   case Accordion:  
  58.   animateAccordion(mLeft, mRight, effectOffset);  
  59.   break;  
  60.   <span style="color:#FF0000;">case</span> <span style="color:#FF0000;">Test:   </span><span class="nf">  
  61. <span style="color:#FF0000;">  animateTest</span></span><span style="color:#FF0000;"><span class="o"></span>(mLeft, mRight, effectOffset);  
  62.   break;  
  63. </span> }  
  64.   
  65. super.onPageScrolled(position, positionOffset, positionOffsetPixels);  
  66.   
  67. if (effectOffset == 0) {  
  68. disableHardwareLayer();  
  69. mState = State.IDLE;  
  70. }  
  71.   
  72. }  

经过这三步,我们就可以添加具有新的补间动画的ViewPager. 这里大家可以尽情发挥自己的创意,不断地扩展该开源库,实现自己想要的效果.

Demo下载地址:http://download.youkuaiyun.com/detail/t12x3456/6468601

        

这个比较详细

一、布局文件

像viewpager一样,直接写一个控件即可。

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:background="#00ff00">

    <com.jfeinstein.jazzyviewpager.JazzyViewPager
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/jazzyViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.jfeinstein.jazzyviewpager.JazzyViewPager>

</LinearLayout>
复制代码

 

它里面还有很多属性可以定义的,像上面的可以用app这个命名空间来定义属性。下面是各种属性,基本就是各种动画,和淡入淡出效果

fadeEnable:是否有淡入淡出效果

outLineEnable:viewpager一个视图周边是否有边框

outLineColor:边框颜色

复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="JazzyViewPager">
        <attr name="style">
            <enum name="standard" value="0" />
            <enum name="tablet" value="1" />
            <enum name="cubein" value="2" />
            <enum name="cubeout" value="3" />
            <enum name="flipvertical" value="4" />
            <enum name="fliphorizontal" value="5" />
            <enum name="stack" value="6" />
            <enum name="zoomin" value="7" />
            <enum name="zoomout" value="8" />
            <enum name="rotateup" value="9" />
            <enum name="rotatedown" value="10" />
            <enum name="accordion" value="11" />
        </attr>
        <attr name="fadeEnabled" format="boolean" />
        <attr name="outlineEnabled" format="boolean" />
        <attr name="outlineColor" format="color|reference" />
    </declare-styleable>

</resources> 
复制代码

 

二、java代码实现

2.1首先看看它有什么动画效果,至于各种动画是什么样的,自己去试试吧~


* Standard
* Tablet
* CubeIn
* CubeOut
* FlipVertical
* FlipHorizontal
* Stack
* ZoomIn
* ZoomOut
* RotateUp
* RotateDown
* Accordion

 

2.2设置动画的方法

viewPager.setTransitionEffect(TransitionEffect.Tablet);

or

TransitionEffect effect = TransitionEffect.valueOf("Tablet");
setupJazziness(effect);

 

2.3简单设置

viewPager.setPageMargin(100);//两个页面之间的间距
viewPager.setFadeEnabled(true);//有淡入淡出效果
viewPager.setOutlineEnabled(true);//有边框
viewPager.setOutlineColor(0xff0000ff);//边框颜色

 

2.4适配器的写法

这里已经写的很简单了,以后照猫画虎就行。主要就是里面需要用这个viewpager做一些事情。

官方文档中说:Due to the limitations of the ViewPager class (which JazzyViewPager is built upon) in order to get the animations to work correctly for more than 3 Views, you'll have to add the following to theinstantiateItem method of your PagerAdapter.

复制代码
private JazzyViewPager mJazzy;
/* ... */
@Override
public Object instantiateItem(ViewGroup container, final int position) {
    Object obj = super.instantiateItem(container, position);
    mJazzy.setObjectForPosition(obj, position);
    return obj;
}
复制代码

 

大概意思就是如果viewpager中的view超过3个了,就需要添加上面这个方法。这里面的obj就是一个view对象,具体参照下面的例子。

例子:

复制代码
    /**
     * @author:Jack Tony
     * @tips  :下面的方法需要注意,尽量按照这种写法。
     * @date  :2014-10-22
     */
    class MyAdapter extends PagerAdapter{

        @Override
        public int getCount() {
            // TODO 自动生成的方法存根
            return 10;
        }

        @Override
        public boolean isViewFromObject(View view, Object obj) {
            if (view instanceof OutlineContainer) {
                return ((OutlineContainer) view).getChildAt(0) == obj;
            } else {
                return view == obj;
            }
        }
        
        /* 如果viewpager中的view超过3个就需要重写这个方法*/  
        @Override  
        public Object instantiateItem(ViewGroup container, final int position) {  
            TextView tv = new TextView(getApplicationContext());
            tv.setText("Page " + (position + 1));
            tv.setTextColor(Color.parseColor("#000000"));
            tv.setTextSize(30);
            tv.setGravity(Gravity.CENTER);
            tv.setBackground(getWallpaper());
            container.addView(tv, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            
            //这句话必须要写,按不同的位置添加视图
            viewPager.setObjectForPosition(tv, position);
            return tv;  
        }
        
        @Override
        public void destroyItem(ViewGroup container, int position, Object obj) {
            container.removeView(viewPager.findViewFromObject(position));
        }


    }
复制代码

 

ok,这样就基本搞定了。贴上全部代码:

MainActivity.java

复制代码
package com.kale.jazzviewpagertest;

import android.app.ActionBar.LayoutParams;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.jfeinstein.jazzyviewpager.JazzyViewPager;
import com.jfeinstein.jazzyviewpager.JazzyViewPager.TransitionEffect;
import com.jfeinstein.jazzyviewpager.OutlineContainer;

public class MainActivity extends Activity {

    private JazzyViewPager viewPager;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        viewPager = (JazzyViewPager)findViewById(R.id.jazzyViewPager);
        /**
         * 添加动画效果,可以设置很多动画效果。如:
         * Standard
         * Tablet
         * CubeIn
         * CubeOut
         * FlipVertical
         * FlipHorizontal
         * Stack
         * ZoomIn
         * ZoomOut
         * RotateUp
         * RotateDown
         * Accordion
         * 
         * 下面是可以通过字符串来产生动画对象的例子:
         * TransitionEffect effect = TransitionEffect.valueOf("Tablet");
         * setupJazziness(effect);
         */
        viewPager.setTransitionEffect(TransitionEffect.Tablet);
        viewPager.setPageMargin(100);//两个页面之间的间距
        viewPager.setFadeEnabled(true);//有淡入淡出效果
        viewPager.setOutlineEnabled(true);//有边框
        viewPager.setOutlineColor(0xff0000ff);//边框颜色
        /**
         * 设置适配器
         * 但当ViewPager中的子View超过三个的时候,我们需要对PagerAdapter修改,
         * 重写InstantiateItem()方法
         */
        viewPager.setAdapter(new MyAdapter());
        
        viewPager.setOnPageChangeListener(null);//可以监听page的改变
    }
    
    /**
     * @author:Jack Tony
     * @tips  :下面的方法需要注意,尽量按照这种写法。
     * @date  :2014-10-22
     */
    class MyAdapter extends PagerAdapter{

        @Override
        public int getCount() {
            // TODO 自动生成的方法存根
            return 10;
        }

        @Override
        public boolean isViewFromObject(View view, Object obj) {
            if (view instanceof OutlineContainer) {
                return ((OutlineContainer) view).getChildAt(0) == obj;
            } else {
                return view == obj;
            }
        }
        
        /* 如果viewpager中的view超过3个就需要重写这个方法*/  
        @Override  
        public Object instantiateItem(ViewGroup container, final int position) {  
            TextView tv = new TextView(getApplicationContext());
            tv.setText("Page " + (position + 1));
            tv.setTextColor(Color.parseColor("#000000"));
            tv.setTextSize(30);
            tv.setGravity(Gravity.CENTER);
            tv.setBackground(getWallpaper());
            container.addView(tv, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            
            //这句话必须要写,按不同的位置添加视图
            viewPager.setObjectForPosition(tv, position);
            return tv;  
        }
        
        @Override
        public void destroyItem(ViewGroup container, int position, Object obj) {
            container.removeView(viewPager.findViewFromObject(position));
        }


    }
}
复制代码

 

最终效果:

 

扩展:

 

我们当然可以继续扩展动画,下面就扩展一个动画。

一、先定义一个动画名字,这里叫test

复制代码
public enum TransitionEffect {
        Standard,
        Tablet,
        CubeIn,
        CubeOut,
        FlipVertical,
        FlipHorizontal,
        Stack,
        ZoomIn,
        ZoomOut,
        RotateUp,
        RotateDown,
        Accordion,
        Test;}
复制代码

 

二、写实现方式

复制代码
protected void animateTest(View left, View right, float positionOffset) {    
 if (mState != State.IDLE) {
  if (left != null) {
   //此处增加具体动画
 }
  if (right != null) {
   //此处增加具体动画实现 
 }
 }
}
复制代码

 

这里举一个源码中table的例子

复制代码
    protected void animateTablet(View left, View right, float positionOffset) {        
        if (mState != State.IDLE) {
            if (left != null) {
                manageLayer(left, true);
                mRot = 30.0f * positionOffset;
                mTrans = getOffsetXForRotation(mRot, left.getMeasuredWidth(),
                        left.getMeasuredHeight());
                ViewHelper.setPivotX(left, left.getMeasuredWidth()/2);
                ViewHelper.setPivotY(left, left.getMeasuredHeight()/2);
                ViewHelper.setTranslationX(left, mTrans);
                ViewHelper.setRotationY(left, mRot);
                logState(left, "Left");
            }
            if (right != null) {
                manageLayer(right, true);
                mRot = -30.0f * (1-positionOffset);
                mTrans = getOffsetXForRotation(mRot, right.getMeasuredWidth(), 
                        right.getMeasuredHeight());
                ViewHelper.setPivotX(right, right.getMeasuredWidth()*0.5f);
                ViewHelper.setPivotY(right, right.getMeasuredHeight()*0.5f);
                ViewHelper.setTranslationX(right, mTrans);
                ViewHelper.setRotationY(right, mRot);
                logState(right, "Right");
            }
        }
    }
复制代码

 

三、添加实现代码到设置项中

复制代码
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
  if (mState == State.IDLE && positionOffset > 0) {
  oldPage = getCurrentItem();
  mState = position == oldPage ? State.GOING_RIGHT : State.GOING_LEFT;
}
boolean goingRight = position == oldPage;    
if (mState == State.GOING_RIGHT && !goingRight)
  mState = State.GOING_LEFT;
  else if (mState == State.GOING_LEFT && goingRight)
  mState = State.GOING_RIGHT;

  float effectOffset = isSmall(positionOffset) ? 0 : positionOffset;

  // mLeft = getChildAt(position);
  // mRight = getChildAt(position+1);
  mLeft = findViewFromObject(position);
  mRight = findViewFromObject(position+1);

  if (mFadeEnabled)
  animateFade(mLeft, mRight, effectOffset);
  if (mOutlineEnabled)
  animateOutline(mLeft, mRight);

  switch (mEffect) {
  case Standard:
  break;
  case Tablet:
  animateTablet(mLeft, mRight, effectOffset);
  break;
  case CubeIn:
  animateCube(mLeft, mRight, effectOffset, true);
  break;
  case CubeOut:
  animateCube(mLeft, mRight, effectOffset, false);
  break;
  case FlipVertical:
  animateFlipVertical(mLeft, mRight, positionOffset, positionOffsetPixels);
  break;
  case FlipHorizontal:
  animateFlipHorizontal(mLeft, mRight, effectOffset, positionOffsetPixels);
  case Stack:
  animateStack(mLeft, mRight, effectOffset, positionOffsetPixels);
  break;
  case ZoomIn:
  animateZoom(mLeft, mRight, effectOffset, true);
  break;
  case ZoomOut:
  animateZoom(mLeft, mRight, effectOffset, false);
  break;
  case RotateUp:
  animateRotate(mLeft, mRight, effectOffset, true);
  break;
  case RotateDown:
  animateRotate(mLeft, mRight, effectOffset, false);
  break;
  case Accordion:
  animateAccordion(mLeft, mRight, effectOffset);
  break;
  case Test:  
  animateTest(mLeft, mRight, effectOffset);
break; } super.onPageScrolled(position, positionOffset, positionOffsetPixels); if (effectOffset == 0) { disableHardwareLayer(); mState = State.IDLE; } }
复制代码

 

原理和思路讲解:http://blog.youkuaiyun.com/lmj623565791/article/details/38026503

 

源码下载:http://download.youkuaiyun.com/detail/shark0017/8070139

 

参考自:http://blog.youkuaiyun.com/t12x3456/article/details/13290349

                     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值