在Android中使用XML创建3D翻转动画

31
我使用了这个安卓教程创建了一个视图的3D翻转效果:this android tutorial。 然而,我是通过编程实现的,并且如果有可能,我想在xml中完成它。 我不是指简单地将视图缩小到中间然后再放大,而是实现真正的3D翻转效果。
请问这是否可以通过xml实现?
8个回答

62

以下是答案,但只适用于3.0及以上版本。

1)创建一个名为“animator”的新资源文件夹。

2)创建一个新的.xml文件,我将其称为“flipping”。使用以下xml代码:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:valueFrom="0" android:valueTo="360" android:propertyName="rotationY" >
</objectAnimator>

不,objectAnimator标签不以大写字母“O”开头。

3)使用以下代码启动动画:

ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator(mContext, R.animator.flipping); 
anim.setTarget(A View Object reference goes here i.e. ImageView);
anim.setDuration(3000);
anim.start();

我是从这里获取的所有内容。


5
要使用下面这段代码的版本低于3.0,请使用以下库:https://github.com/JakeWharton/NineOldAndroids。由用户http://stackoverflow.com/users/1117511/loks贡献。 - Swifty McSwifterton
它能工作,但只对一个视图有效,仅翻转了一次,第二次对于相同的视图它就不起作用了。 - Krunal Shah
@KrunalShah 我也遇到了同样的问题 :) 这个链接解决了它:https://dev59.com/6VXTa4cB1Zd3GeqPzz8u#5317467 - Lo-Tan
2
@KrunalShah 实际上,只需在objectAnimator声明中添加android:valueFrom="0"即可解决问题。 - Lo-Tan
2
这会导致一个相当幼稚的翻转,应该实现一些缩放来使其更具视觉吸引力。 - A. Steenbergen
显示剩余3条评论

30

由于这个问题的答案相当陈旧,这里提供一个更现代的解决方案,依赖于ValueAnimators。 该解决方案实现了真正美观的3D翻转效果,因为它不仅会翻转视图,还会在翻转时对其进行缩放(这是苹果的做法)。

首先,我们设置了ValueAnimator:

mFlipAnimator = ValueAnimator.ofFloat(0f, 1f);
mFlipAnimator.addUpdateListener(new FlipListener(frontView, backView));

相应的更新监听器:

public class FlipListener implements ValueAnimator.AnimatorUpdateListener {

    private final View mFrontView;
    private final View mBackView;
    private boolean mFlipped;

    public FlipListener(final View front, final View back) {
        this.mFrontView = front;
        this.mBackView = back;
        this.mBackView.setVisibility(View.GONE);
    }

    @Override
    public void onAnimationUpdate(final ValueAnimator animation) {
        final float value = animation.getAnimatedFraction();
        final float scaleValue = 0.625f + (1.5f * (value - 0.5f) * (value - 0.5f));

        if(value <= 0.5f){
            this.mFrontView.setRotationY(180 * value);
            this.mFrontView.setScaleX(scaleValue);
            this.mFrontView.setScaleY(scaleValue);
            if(mFlipped){
                setStateFlipped(false);
            }
        } else {
            this.mBackView.setRotationY(-180 * (1f- value));
            this.mBackView.setScaleX(scaleValue);
            this.mBackView.setScaleY(scaleValue);
            if(!mFlipped){
                setStateFlipped(true);
            }
        }
    }

    private void setStateFlipped(boolean flipped) {
        mFlipped = flipped;
        this.mFrontView.setVisibility(flipped ? View.GONE : View.VISIBLE);
        this.mBackView.setVisibility(flipped ? View.VISIBLE : View.GONE);
    }
}

就是这样!

进行此设置后,您可以通过调用以下方法翻转视图:

mFlipAnimator.start();

通过调用反转翻转

mFlipAnimator.reverse();

如果你想检查视图是否翻转,请实现并调用此函数:

private boolean isFlipped() {
    return mFlipAnimator.getAnimatedFraction() == 1;
}

您还可以通过实现此方法来检查视图当前是否正在翻转:

您可以通过实现此方法来检查视图当前是否正在翻转:

private boolean isFlipping() {
    final float currentValue = mFlipAnimator.getAnimatedFraction();
    return (currentValue < 1 && currentValue > 0);
}
你可以组合上述函数来实现一个很好的翻转切换功能,具体取决于它是否已经翻转:
private void toggleFlip() {
    if(isFlipped()){
        mFlipAnimator.reverse();
    } else {
        mFlipAnimator.start();
    }
}

就这样了! 简单易懂。享受吧!


1
@AntonioSesto 抱歉,我大多数情况下不使用xml进行动画制作,因为我觉得在代码中进行动画制作更加灵活。我认为在xml中设置枢轴会很困难,因为你不知道视图的尺寸。也许你可以将其设置为百分比,尝试将枢轴设置为50%p,看看效果如何? - A. Steenbergen
@A.Steenbergen 我该如何更改持续时间? - onCreate
2
这个完美无缺地运行了!谢谢你发布这个答案!感谢帮助!我给你点赞! - Shinta S
2
我花了好几个小时才找到一个答案,它不涉及片段,可以同时动画两个视图而不仅仅是一个,还支持3D,并且可以正反翻转。这就是答案,谢谢! - Justin
1
真的很棒,我使用 Kotlin 在相对布局中进行旋转实现,效果非常完美。谢谢。 - AllanRibas
显示剩余2条评论

8

我已经创建了一个简单的程序,可以创建类似于下面这样的视图翻转:

enter image description here

在Activity中,您需要创建此方法,以添加flip_rotation到视图中。

private void applyRotation(View view) 
{
    final Flip3dAnimation rotation = new Flip3dAnimation(view);
    rotation.applyPropertiesInRotation();
    view.startAnimation(rotation);
}

为此,您需要复制用于提供翻转旋转的主类。
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;

public class Flip3dAnimation extends Animation {
    private final float mFromDegrees;
    private final float mToDegrees;
    private final float mCenterX;
    private final float mCenterY;
    private Camera mCamera;

    public Flip3dAnimation(View view) {
        mFromDegrees = 0;
        mToDegrees = 720;
        mCenterX = view.getWidth() / 2.0f;
        mCenterY = view.getHeight() / 2.0f;
    }

    @Override
    public void initialize(int width, int height, int parentWidth,
            int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCamera = new Camera();
    }

    public void applyPropertiesInRotation()
    {
        this.setDuration(2000);
        this.setFillAfter(true);
        this.setInterpolator(new AccelerateInterpolator());
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float fromDegrees = mFromDegrees;
        float degrees = fromDegrees
                + ((mToDegrees - fromDegrees) * interpolatedTime);

        final float centerX = mCenterX;
        final float centerY = mCenterY;
        final Camera camera = mCamera;

        final Matrix matrix = t.getMatrix();

        camera.save();

        Log.e("Degree",""+degrees) ;
        Log.e("centerX",""+centerX) ;
        Log.e("centerY",""+centerY) ;

        camera.rotateY(degrees);

        camera.getMatrix(matrix);
        camera.restore();

        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);

    }

}

这很好,但最好不要依赖自定义动画函数,而是使用ObjectAnimator。原因是它有更好的支持和可逆性。 - A. Steenbergen
1
@Tushar Pandey:太神啦!这是什么答案,好牛啊。 - Mayank Sugandhi

5

在不使用资源动画的情况下,翻转图像的较好解决方案如下:

  ObjectAnimator animation = ObjectAnimator.ofFloat(YOUR_IMAGEVIEW, "rotationY", 0.0f, 360f);  // HERE 360 IS THE ANGLE OF ROTATE, YOU CAN USE 90, 180 IN PLACE OF IT,  ACCORDING TO YOURS REQUIREMENT 

  animation.setDuration(500); // HERE 500 IS THE DURATION OF THE ANIMATION, YOU CAN INCREASE OR DECREASE ACCORDING TO YOURS REQUIREMENT
  animation.setInterpolator(new AccelerateDecelerateInterpolator());
  animation.start();

5

om252345提供的教程或链接并不能产生令人信服的3D翻转效果。在iOS中所做的不仅仅是简单的沿y轴旋转。为了创造出漂亮的翻转效果,还需要进行缩放效果。因此,请参考这个示例。 此外,这里还有一个视频。


我知道这个问题是关于如何在android.com的示例中使用xml使y轴旋转。但是我想也许你也对此感兴趣。 - Ephraim

3
  1. The simplest way to do it is using ViewPropertyAnimator

    mImageView.animate().rotationY(360f);
    

    Using the fluent interface you can build more complex and exciting animation. E.g. you can enable hardware acceleration just call withLayer() method(API 16). More here

  2. If you want to figure out how to create 3d flick animation, please follow here and here

  3. I implemended my own solution only for a research. It includes: cancelation, accelleration, support API >= 15 and is based on Property Animation. The entire animation includes 4 parts, 2 for each side. Every objectAnimator has a listener that defines current animation index and represents an image in the onAnimationStart and current play time value in the onAnimationCancel. It looks like

    mQuarterAnim1.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            mQuarterCurrentAnimStartIndex = QUARTER_ANIM_INDEX_1;
            mImageView.setImageResource(mResIdFrontCard);
        }
    
        @Override
        public void onAnimationCancel(Animator animation) {
            mQuarterCurrentAnimPlayTime = ((ObjectAnimator) animation).getCurrentPlayTime();
        }
    });
    

    For start set call

    mAnimatorSet.play(mQuarterAnim1).before(mQuarterAnim2)
    

    If AnimatorSet was canceled we can calculate delta and run the reverse animation relying on the current index animation and the current play time value.

    long degreeDelta = mQuarterCurrentAnimPlayTime * QUARTER_ROTATE / QUARTER_ANIM_DURATION;
    
    if (mQuarterCurrentAnimStartIndex == QUARTER_ANIM_INDEX_1) {
        mQuarterAnim4.setFloatValues(degreeDelta, QUARTER_FROM_1);
        mQuarterAnim4.setDuration(mQuarterCurrentAnimPlayTime);
    
        mAnimatorSet.play(mQuarterAnim4);
    }
    

您可以在这里找到完整的代码片段。


不错的解释 @yoAlex5 +1 :) - Ravindra Kushwaha

2
只需将您要进行动画的视图放置在viewToFlip的位置即可。最初的回答。
ObjectAnimator flip = ObjectAnimator.ofFloat(viewToFlip, "rotationY", 0f, 360f); // or rotationX
flip.setDuration(2000); // 2 seconds
flip.start();

我正在修改您的答案,因为它的质量较差。您应该在代码中添加解释。 - DaFois

0

在A. Steenbergen的优秀答案中补充一点。当翻转同一个视图(例如更新TextView)时,我在构造函数中删除了View.Visibility的更改,以保持过渡更加平滑。

public FlipListener(final View front, final View back) {
    this.mFrontView = front;
    this.mBackView = back;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接