Reputation: 5506
I'm trying to animate transaction between two fragments, with this code: http://developer.android.com/training/animation/cardflip.html
But the result is exactly this: http://developer.android.com/training/animation/anim_card_flip.mp4
However, I want this result: https://www.youtube.com/watch?v=52mXHqX9f3Y
The difference, is that even tho both rotate 180º, the second one does it with a different camera (Z-Axis).
So the question is:
Thanks.
Edit: Check differences.
Upvotes: 8
Views: 7096
Reputation: 5367
I tried to follow a.bertucci's answer but the animation doesn't work, I don't know why... I am a bit new to this world and a bit hard for me to understand what is going on about the custom FlippableFrameLayout.
But inspired by his answer on Camera distance, I found that I just need to impose one more animation on the scale property. (i.e. scaleX and scaleY)
Therefore, with everything else the same as official tutorial here, you just need to modify the animator xml files to achieve the desired effect:
For left_out.xml and right_out.xml, add the following:
<objectAnimator
android:valueFrom="1f"
android:valueTo="0.5f"
android:propertyName="scaleX"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_half" />
<objectAnimator
android:valueFrom="1f"
android:valueTo="0.5f"
android:propertyName="scaleY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_half" />
For left_in.xml and right_in.xml, add the following:
<objectAnimator
android:valueFrom="0.5f"
android:valueTo="1.0f"
android:propertyName="scaleX"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:startOffset="@integer/card_flip_time_half"
android:duration="@integer/card_flip_time_half" />
<objectAnimator
android:valueFrom="0.5f"
android:valueTo="1.0f"
android:propertyName="scaleY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:startOffset="@integer/card_flip_time_half"
android:duration="@integer/card_flip_time_half" />
And of course, you can modify the valueFrom and valueTo here (I used 0.5f), to adjust the "camera distance".
Upvotes: 1
Reputation: 12142
To achieve what you want you need to do two more things in your animators:
In both cases you need to know the size of your view, so I would recommend to create your custom layout component to be used as root of your fragments, exposing a set of properties you can animate using different objectanimator
inside your xml.
The component should look like this:
public class FlippableLayout extends FrameLayout {
private FlipEvaluator flipRightInEvaluator;
private FlipEvaluator flipRightOutEvaluator;
private FlipEvaluator flipLeftInEvaluator;
private FlipEvaluator flipLeftOutEvaluator;
public FlippableLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FlippableLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setCameraDistance(getCameraDistance() * 10); // reduces perspective skewing
flipRightInEvaluator = new FlipEvaluator(
1f, .5f, // pivotX/pivotY
-1f, 0f, // translationX start/end
-180, 0, // rotationY start/end
0f, 1f); // alpha start/end
flipRightOutEvaluator = new FlipEvaluator(
0f, .5f,
0f, 1f,
0, 180,
1f, 0f);
flipLeftInEvaluator = new FlipEvaluator(
.0f, .5f,
1f, 0f,
180, 0,
0f, 1f);
flipLeftOutEvaluator = new FlipEvaluator(
1f, .5f,
0f, -1f,
0, -180,
1f, 0f);
}
public void setFlipRightIn(float value) {
evaluateUsing(flipRightInEvaluator, value);
}
public void setFlipRightOut(float value) {
evaluateUsing(flipRightOutEvaluator, value);
}
public void setFlipLeftIn(float value) {
evaluateUsing(flipLeftInEvaluator, value);
}
public void setFlipLeftOut(float value) {
evaluateUsing(flipLeftOutEvaluator, value);
}
private void evaluateUsing(FlipEvaluator evaluator, float value) {
float cappedValue = Math.min(1f, Math.max(0f, value));
setPivotX(getWidth() * evaluator.getPivotX());
setPivotY(getHeight() * evaluator.getPivotY());
setAlpha(evaluator.getAlpha(cappedValue));
setTranslationX(getWidth() * evaluator.getTranslationX(cappedValue));
setRotationY(evaluator.getRotationY(cappedValue));
}
private static class FlipEvaluator {
private final float pivotX;
private final float pivotY;
private final float startTranslationX;
private final float endTranslationY;
private final float startRotationY;
private final float endRotationY;
private final float startAlpha;
private final float endAlpha;
/**
* Simple evaluator holding all the start/end values for a flip animation.
*
* @param pivotX value between 0 and 1, where 0 is the left border and 1 is the right border of the target
* @param pivotY value between 0 and 1, where 0 is the top border and 1 is the bottom border of the target
* @param startTranslationX value between 0 and 1, where 1 is the width of the target
* @param endTranslationY value between 0 and 1, where 1 is the width of the target
* @param startRotationY value between -180 and 180
* @param endRotationY value between -180 and 180
* @param startAlpha initial alpha
* @param endAlpha final alpha
*/
private FlipEvaluator(float pivotX, float pivotY,
float startTranslationX, float endTranslationY,
float startRotationY, float endRotationY,
float startAlpha, float endAlpha) {
this.pivotX = pivotX;
this.pivotY = pivotY;
this.startTranslationX = startTranslationX;
this.endTranslationY = endTranslationY;
this.startRotationY = startRotationY;
this.endRotationY = endRotationY;
this.startAlpha = startAlpha;
this.endAlpha = endAlpha;
}
public float getPivotX() {
return pivotX;
}
public float getPivotY() {
return pivotY;
}
public float getTranslationX(float t) {
return startTranslationX + (endTranslationY - startTranslationX) * t;
}
public float getRotationY(float t) {
return startRotationY + (endRotationY - startRotationY) * t;
}
public float getAlpha(float t) {
return t < .5f ? startAlpha : endAlpha;
}
}
}
Your animation files will look like this:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="1"
android:propertyName="flipLeftIn"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="1000" />
Of course you can change flipLeftIn
with flipLeftOut
, flipRightIn
or flipRightOut
in order to apply the animator to a different property.
In your Activity
you can set custom animations in your fragment transaction as usual, specifying the XMLs you previously defined:
....
getFragmentManager()
.beginTransaction()
.setCustomAnimations(
R.animator.card_flip_right_in, R.animator.card_flip_right_out,
R.animator.card_flip_left_in, R.animator.card_flip_left_out)
....
The other approach is to do everything in the XML, but setting the pivot/translation using a dimension value defined via XML is not as scalable as the solution shown above.
EDIT To reduce the camera distance you can easily use View.setCameraDistance() on API>12. I updated the snippet including this change.
Upvotes: 9