Android動畫(ViewHelp/3D旋轉)

如果想具體瞭解Android動畫的可參考小編的上一篇博客,在這裏我們僅爲實現頁面的3D旋轉效果做一個小的實現。當然,要說這個3D效果與其他3D效果有哪些不同之處呢。也就是拉大了觀察者的攝像機與動畫之間的距離,避免旋轉效果因Y方向上的頁面旋轉而超出屏幕高度,使整體效果看起來更舒心而已,除此以外還有ViewHelp引用動畫的實現。

3D縮放旋轉效果

  • 設置攝像頭距離
  • 根據中心位置縮放頁面視圖
  • 繞頁面中心Y軸旋轉

以上動畫效果實現分爲以上幾個步驟,而其中最需要注意的是旋轉角度的設計。如果將頁面執行水平旋轉180°(比如說0°-180°),那麼結果將導致頁面的內容也會出現對稱的效果,這個可以自行測試下。那麼該如何解決這種問題呢?小編的想法是當頁面旋轉至90°(或者270°)的時候,對應的將當前頁面的角度設置爲270°(或者90°)。然後再繼續執行剩下的90°(180°-90°),最後抵達我們想要的角度爲360°(或者0°)。

爲了解決上訴的問題,小編試過從ObjectAnimator入手,試着爲其添加更多的變化值。如ObjectAnimator.ofFloat(mMainFly, “rotationY”, 0, 90, 270, 360);當然結果肯定是不行的,因爲每個角度的變換過程所消耗的時間是等同的,這麼做只會導致頁面內容的不正常閃爍。所以,最好的方法就是分開執行,且在每個動畫單元執行結束後通過setRotationY();的方法設置下個單元的起始角度。下面我們通過Fragment的切換實現上圖效果。

@ContentView(R.layout.activity_r3d_frame)
public class R3DFragmentActivity extends FragmentActivity {

    @ViewInject(R.id.r3d_fly_main)
    private FrameLayout mMainFly;

    private R3DFirstFragment mFirstFragment;
    private R3DSecondFragment mSecondFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ViewUtils.inject(this);
        showDefaultFragment();
    }

    /**
     * 默認顯示FirstFragment
     */
    public void showDefaultFragment() {
        if (mFirstFragment == null) {
            mFirstFragment = new R3DFirstFragment();
        }
        putFragment(R.id.r3d_fly_main, mFirstFragment, "r3d_fly_main");
    }

    /**
     * 顯示SecondFragment
     */
    public void showSecondFragment() {
        if (mSecondFragment == null) {
            mSecondFragment = new R3DSecondFragment();
        }
        showFragmentAdd(R.id.r3d_fly_main, mSecondFragment, "r3d_fly_main");
    }

    /**
     * 爲頁面跳轉及其切換添加3D旋轉效果
     */
    public void showCutAnim(final boolean is2Second) {

        final int time = 600;
        final int distance = 15000;
        final int scaleTime = 80;
        final float scale = 0.9f;

        // 設置照相機的距離(避免旋轉時高度超出屏幕高度)
        float CameraScale = getResources().getDisplayMetrics().density * distance;
        mMainFly.setCameraDistance(CameraScale);

        ObjectAnimator animatorR1;
        if (is2Second) {
            animatorR1 = ObjectAnimator.ofFloat(mMainFly, "rotationY", 0, 90).setDuration(time / 2);
        } else {
            animatorR1 = ObjectAnimator.ofFloat(mMainFly, "rotationY", 0, -90).setDuration(time / 2);
        }
        ObjectAnimator animatorR2;
        if (is2Second) {
            animatorR2 = ObjectAnimator.ofFloat(mMainFly, "rotationY", 270, 360).setDuration(time / 2);
        } else {
            animatorR2 = ObjectAnimator.ofFloat(mMainFly, "rotationY", -270, -360).setDuration(time / 2);
        }
        ObjectAnimator animatorSX1 = ObjectAnimator.ofFloat(mMainFly, "scaleX", 1.0f, scale).setDuration(scaleTime);
        ObjectAnimator animatorSY1 = ObjectAnimator.ofFloat(mMainFly, "scaleY", 1.0f, scale).setDuration(scaleTime);
        ObjectAnimator animatorSX2 = ObjectAnimator.ofFloat(mMainFly, "scaleX", scale, 1.0f).setDuration(scaleTime);
        ObjectAnimator animatorSY2 = ObjectAnimator.ofFloat(mMainFly, "scaleY", scale, 1.0f).setDuration(scaleTime);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(animatorSX1, animatorSY1);
        animatorSet.playTogether(animatorSX2, animatorSY2);
        animatorSet.play(animatorR1).after(animatorSX1);
        animatorSet.play(animatorR1).before(animatorR2);
        animatorSet.play(animatorR2).before(animatorSX2);
        animatorSet.start();

        animatorR1.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if (is2Second) {
                    showSecondFragment();
                } else {
                    onBackPressed();
                    mCurrentFragment = mFirstFragment;
                    mMainFly.setRotationY(-270);
                }
            }
        });
    }
}

除了Fragment以外,我們也可以手動修改常用ViewPager,來實現類似上圖的動畫效果。首先,我們要新建一個.class繼承自ViewPager.PageTransformer,重寫transformPage(View view, float position);方法來控制當前卡片view的狀態,這裏需要注意的只是position的變化跟蹤,因爲ViewHelp的協助來共同完成動畫的展現,相比上面的實現,無法爲各個動畫單元提供各自的執行時長和順序。最後使用ViewPager.setPageTransformer();方法就可以實現卡片切換的動畫效果啦。

public class FilpTransformer implements ViewPager.PageTransformer {

    private Context mContext;
    private static final float MIN_SCALE = 0.9f;

    public FilpTransformer(Context mContext) {
        this.mContext = mContext;
    }

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

        float pageWidth = view.getMeasuredWidth();
        float pageHeight = view.getMeasuredHeight();

        // 設置照相機的距離(避免旋轉時高度超出屏幕高度)
        final int distance = 15000;
        float CameraScale = mContext.getResources().getDisplayMetrics().density * distance;
        view.setCameraDistance(CameraScale);

        // 縮放
        float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
        view.setScaleX(scaleFactor);
        view.setScaleY(scaleFactor);

        // 左側Page 1 <---- 0 -----> -1 右側Page
        ViewHelper.setPivotY(view, pageHeight * 0.5f);
        ViewHelper.setRotationY(view, 180f * position);
        if (position <= 0) {
            view.setAlpha(1 - (Math.abs(position)));
            ViewHelper.setPivotX(view, pageWidth);
            if (position <= -1) {
                // 旋轉完成將另一個Fragment以90°的位置放置,避免重疊
                ViewHelper.setRotationY(view, 90f * position);
            }
        } else {
            ViewHelper.setPivotX(view, 0);
            if (position == 1) {
                ViewHelper.setRotationY(view, 90f * position);
            }
        }
    }
}
發佈了50 篇原創文章 · 獲贊 4 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章