We all know this article of how to create "card filp" animations using new api.
But how can I make this on apis < 3.0?
Update:
As long as there are good and easy-to-use libraries like android-FlipView I don't think you really need to do such animation by yourself.
Found the answer. If you want to do flip animation on ALL ANDROID VERSIONS, use this:
Activity layout file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_activity_root"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/transparent" >
<RelativeLayout
android:id="#+id/main_activity_card_face"
android:layout_width="300dp"
android:layout_height="407dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="#drawable/front"
android:clickable="true"
android:onClick="onCardClick"
android:padding="5dp" >
</RelativeLayout>
<RelativeLayout
android:id="#+id/main_activity_card_back"
android:layout_width="300dp"
android:layout_height="407dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="#drawable/back"
android:clickable="true"
android:onClick="onCardClick"
android:visibility="gone" >
</RelativeLayout>
</RelativeLayout>
As the layout file flips two view groups you could put anything else inside the view group and it should work. Now lets look at the methods inside the activity which handles calling the flip animation code:
public void onCardClick(View view)
{
flipCard();
}
private void flipCard()
{
View rootLayout = findViewById(R.id.main_activity_root);
View cardFace = findViewById(R.id.main_activity_card_face);
View cardBack = findViewById(R.id.main_activity_card_back);
FlipAnimation flipAnimation = new FlipAnimation(cardFace, cardBack);
if (cardFace.getVisibility() == View.GONE)
{
flipAnimation.reverse();
}
rootLayout.startAnimation(flipAnimation);
}
And finally the FlipAnimation class:
public class FlipAnimation extends Animation
{
private Camera camera;
private View fromView;
private View toView;
private float centerX;
private float centerY;
private boolean forward = true;
/**
* Creates a 3D flip animation between two views.
*
* #param fromView First view in the transition.
* #param toView Second view in the transition.
*/
public FlipAnimation(View fromView, View toView)
{
this.fromView = fromView;
this.toView = toView;
setDuration(700);
setFillAfter(false);
setInterpolator(new AccelerateDecelerateInterpolator());
}
public void reverse()
{
forward = false;
View switchView = toView;
toView = fromView;
fromView = switchView;
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight)
{
super.initialize(width, height, parentWidth, parentHeight);
centerX = width/2;
centerY = height/2;
camera = new Camera();
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
// Angle around the y-axis of the rotation at the given time
// calculated both in radians and degrees.
final double radians = Math.PI * interpolatedTime;
float degrees = (float) (180.0 * radians / Math.PI);
// Once we reach the midpoint in the animation, we need to hide the
// source view and show the destination view. We also need to change
// the angle by 180 degrees so that the destination does not come in
// flipped around
if (interpolatedTime >= 0.5f)
{
degrees -= 180.f;
fromView.setVisibility(View.GONE);
toView.setVisibility(View.VISIBLE);
}
if (forward)
degrees = -degrees; //determines direction of rotation when flip begins
final Matrix matrix = t.getMatrix();
camera.save();
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
Here is the link for original post:
Displaying card flip animation on old android
UPDATE from #FMMobileFelipeMenezes .
if you want the animation with a smooth scale to flip, change this part of code to (applyTransformation) :
final Matrix matrix = t.getMatrix();
camera.save();
camera.translate(0, 0, Math.abs(degrees)*2);
camera.getMatrix(matrix);
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
UPDATE from #Hesam
There is good tutorial that I recommend to read it. Although it's not as nice as Android tutorial based on fragments, it's worth to be read and useful if you want to assign animation to layouts and views as well as have it on old APIs.
Use Android's scale animation to simulate a 3D flip
Improved project on github by #LenaBru
I used Flextra code below, and if you want the animation with a smooth scale to flip, change this part of code to (applyTransformation) :
final Matrix matrix = t.getMatrix();
camera.save();
camera.translate(0, 0, Math.abs(degrees)*2);
camera.getMatrix(matrix);
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
I played with this all day, and finally achieved the ultimate goal - a smooth cardflip like rotation animation of two views!
I put demo project here
public class FlipAnimation extends Animation {
private Camera camera;
private View fromView;
private View toView;
private float centerX;
private float centerY;
private boolean forward = true;
/**
* Creates a 3D flip animation between two views.
*
* #param fromView
* First view in the transition.
* #param toView
* Second view in the transition.
*/
public FlipAnimation(View fromView, View toView) {
this.fromView = fromView;
this.toView = toView;
setDuration(1500);
setFillAfter(false);
// setInterpolator(new AccelerateDecelerateInterpolator());
setInterpolator(new LinearInterpolator());
}
public void reverse() {
if (forward) {
View switchView = toView;
toView = fromView;
fromView = switchView;
}
forward = false;
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
centerX = width / 2;
centerY = height / 2;
camera = new Camera();
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
// Angle around the y-axis of the rotation at the given time
// calculated both in radians and degrees.
final double radians = Math.PI * interpolatedTime;
float degrees = (float) (180.0 * radians / Math.PI);
//scale down the views a bit, so that they would look nice when the rotation begins
if (interpolatedTime <= 0.05f) {
fromView.setScaleX(1 - interpolatedTime);
fromView.setScaleY(1 - interpolatedTime);
toView.setScaleX(1 - interpolatedTime);
toView.setScaleY(1 - interpolatedTime);
}
// Once we reach the midpoint in the animation, we need to hide the
// source view and show the destination view. We also need to change
// the angle by 180 degrees so that the destination does not come in
//It is very important to call "toView.bringToFront()" and not play with the
// visibility of the views, because if you apply this animation more than once,
//the subsequent calls may fail
if (interpolatedTime >= 0.5f) {
degrees -= 180.f;
toView.bringToFront();
//these two lines force a layout redraw
((View)toView.getParent()).requestLayout();
((View)toView.getParent()).invalidate();
}
//scale the views back to their original size (Assuming original size was 1)
if (interpolatedTime >= 0.95f) {
fromView.setScaleX(interpolatedTime);
fromView.setScaleY(interpolatedTime);
toView.setScaleX(interpolatedTime);
toView.setScaleY(interpolatedTime);
}
if (forward)
degrees = -degrees; // determines direction of rotation when flip
// begins
final Matrix matrix = t.getMatrix();
camera.save();
camera.translate(0, 0, Math.abs(degrees) * 2);
camera.getMatrix(matrix);
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
and call it like this
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
public class MainActivity extends FragmentActivity {
private boolean showingBack;
private FragmentLeft left = new FragmentLeft();
private FragmentRight right = new FragmentRight();
private Context context;
private Handler handler;
private FlipAnimation flipAnimation;
private FlipAnimation backFlip;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
handler = new Handler(getMainLooper());
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, right, "fragmentRight").commit();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, left, "fragmentLeft").commit();
findViewById(R.id.flip).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
flipAnimation = new FlipAnimation(left.getView(), right.getView());
backFlip = new FlipAnimation(left.getView(), right.getView());
handler.removeCallbacks(rotate);
handler.postDelayed(rotate, 100);
}
});
}
private Runnable rotate = new Runnable() {
#Override
public void run() {
//put a variable showingBack, do not rely on view properties to flip
if (!showingBack) {
//very important to flip both views, so that when the
//left view goes to back and right view goes to front,
//the right view finishes the rotation
left.getView().startAnimation(flipAnimation);
right.getView().startAnimation(flipAnimation);
Toast.makeText(context, "flip", Toast.LENGTH_LONG).show();
showingBack = true;
} else {
showingBack = false;
backFlip.reverse();
Toast.makeText(context, "backflip", Toast.LENGTH_LONG).show();
//very important to flip both views, so that when the
//right view goes to back and right view goes to front,
//the left view finishes the rotation
left.getView().startAnimation(backFlip);
right.getView().startAnimation(backFlip);
}
}
};
}
These are the fragments
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class FragmentRight extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_right, container,false);
}
}
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class FragmentLeft extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_left, container,false);
}
}
and finally the view itself
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:background="#ff151515"
tools:context="com.example.flipviewtest.MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true" >
</FrameLayout>
<Button
android:id="#+id/flip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="flip" />
</RelativeLayout>
fragment_left.xml
<?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="#ffff0000"
>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0ffff0"
android:layout_margin="20dp" />
</LinearLayout>
fragment_right.xml
<?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:background="#ff00ff00"
android:orientation="vertical" >
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:background="#ff0000ff" />
</LinearLayout>
note some of the code taken from Flextra's and #FMMobileFelipeMenezes answers
There is good tutorial that I recommend to read it. Although it's not as nice as Android tutorial based on fragments, it's worth to be read and useful if you want to assign animation to layouts and views as well as have it on old APIs.
Use Android's scale animation to simulate a 3D flip
Related
For having effects I scale up the child view which cause child view to go out side of its parent view. I have a button in child view, it works before scaling but after scaling doesn't work. what is going wrong? see image below:
for scaling child I use this code:
childView.bringToFront();
Animation a = new Animation() {
#Override
protected void applyTransformation(float t, Transformation trans) {
float scale = 1f * ( 1 - t ) + SCALE_UP_FACTOR * t;
childView.setScaleX(scale);
childView.setScaleY(scale);
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(ANIM_DURATION);
a.setInterpolator(new Interpolator() {
#Override
public float getInterpolation(float t) {
t -= 1f;
return (t * t * t * t * t) + 1f; // (t-1)^5 + 1
}
});
childView.startAnimation(a);
the parent is a ViewPager :
<ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/invoice_list_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f5f5f5"
android:layout_gravity="center"
android:clipChildren="false"
android:clipToPadding="false"
/>
This should do the trick:
final View grandParent = (View) childView.getParent().getParent();
grandParent.post(new Runnable() {
public void run() {
Rect offsetViewBounds = new Rect();
childView.getHitRect(offsetViewBounds);
// After scaling you probably want to append your view to the new size.
// in your particular case it probably could be only offsetViewBounds.right:
// (animDistance - int value, which you could calculate from your scale logic)
offsetViewBounds.right = offsetViewBounds.right + animDistance;
// calculates the relative coordinates to the parent
((ViewGroup)parent).offsetDescendantRectToMyCoords(childView, offsetViewBounds);
grandParent.setTouchDelegate(new TouchDelegate(offsetViewBounds, childView));
}
});
Though I'm not sure whether it will work with Animation, but for scaling you could use something like that instead:
float scale = ...; // your scale logic
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(childView,
PropertyValuesHolder.ofFloat("scaleX", scale),
PropertyValuesHolder.ofFloat("scaleY", scale));
animator.setDuration(ANIM_DURATION);
animator.start();
And pay attention to the line android:clipChildren="false" for your parent view in XML-file.
This is really quite a simple task but all of the solutions I have looked at seem to point me towards creating a custom view which extends imageview. Which is frankly ridiculous. Here is what I want to do:
My animation:
public void spin() {
float centerX = imageview.getX() + (imageview.getWidth()/2);
float centerY = imageview.getY() + (imageview.getHeight()/2);
Animation animation = new RotateAnimation(0, 360, centerX, centerY);
animation.setRepeatCount(Animation.INFINITE);
imageview.setAnimation(animation);
imageview.animate();
}
My view:
<ImageView
android:id="#+id/imageview"
android:src="#mipmap/app_icon"
android:layout_centerHorizontal="true"
android:layout_above="#+id/progressbar"
android:layout_marginBottom="60dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
getX(), getY(), getWidth() and getHeight() all return 0 at every stage of the fragment lifecycle(onCreateView, onStart, onResume and onActivityCreated).
Is it possible to get the center of my view without creating a custom view?
Trying to get your View's dimensions in your Fragment lifecycle methods is not the right way to go. The easiest way you can do it is to use ViewTreeObserver, like this:
mYourImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int width = mYourImageView.getWidth();
int height = mYourImageView.getHeight();
if (width > 0 && height > 0) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
mYourImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
mYourImageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
//now you've got your dimensions.
}
}
});
I am trying to put a GLSurfaceView and a button into the same xml layout. But my application keep closing automatically whenever I run it on my device.
Could someone please help me and tell what am I missing or what is wrong with my code?
here is my code
===================
<?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:id= "#+id/linearlayout1" >
<Button
android:id="#+id/buttonID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:text="A Button" />
<com.example.test2.MyGLSurfaceView
android:id="#+id/glSurfaceViewID"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.23" />
</LinearLayout>
================
package com.example.test2;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.Menu;
import android.view.MotionEvent;
public class MainActivity extends Activity {
private GLSurfaceView mGLView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create a GLSurfaceView instance and set it
// as the ContentView for this Activity
mGLView = new MyGLSurfaceView(this);
//setContentView(mGLView);
setContentView(R.layout.activity_main);
}
// #Override
protected void onPause() {
super.onPause();
mGLView = (MyGLSurfaceView)findViewById(R.id.glSurfaceViewID);
mGLView.onPause();
}
//
#Override
protected void onResume() {
super.onResume();
mGLView = (MyGLSurfaceView)findViewById(R.id.glSurfaceViewID);
mGLView.onResume();
}
}
class MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer mRenderer;
public MyGLSurfaceView(Context context) {
super(context);
// Create an OpenGL ES 2.0 context.
setEGLContextClientVersion(2);
// Set the Renderer for drawing on the GLSurfaceView
mRenderer = new MyGLRenderer();
setRenderer(mRenderer);
// Render the view only when there is a change in the drawing data
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float mPreviousX;
private float mPreviousY;
#Override
public boolean onTouchEvent(MotionEvent e) {
// MotionEvent reports input details from the touch screen
// and other input controls. In this case, you are only
// interested in events where the touch position changed.
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
// reverse direction of rotation above the mid-line
if (y > getHeight() / 2) {
dx = dx * -1 ;
}
// reverse direction of rotation to left of the mid-line
if (x < getWidth() / 2) {
dy = dy * -1 ;
}
mRenderer.mAngle += (dx + dy) * TOUCH_SCALE_FACTOR; // = 180.0f / 320
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
}
===========================
Thank you.
In MainActivity.onCreate() use findViewById instead of creating a new View.(like u did in onPause, onResume) and only assign the variable once.
setContentView(R.layout.activity_main);
mGLView = (MyGLSurfaceView) findViewById(R.id.glSurfaceViewID);
Make sure MyGLSurfaceView has constructors with AttributeSet so it could be inflated from XML. Check out this stackoverflow atricle for the constructors.
And aswell change the order of the Button and MyGLSurfaceView in the XML, because now Button will be below the MyGLSurfaceView in your layout so you won't be able to see it.
This is a 7 year old QA, this is how you do it now with Kotlin:
import android.content.Context
import android.opengl.GLES20
import android.opengl.GLSurfaceView
import android.util.AttributeSet
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
class GameView
#JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
) : GLSurfaceView(context, attrs) {
companion object{
// Support OpenGL ES 2.0
private const val OPEN_GLES_VERSION = 2
}
private val renderer: GameRenderer
init {
//create an OpenGL ES 2.0-compatible context.
setEGLContextClientVersion(OPEN_GLES_VERSION)
// Set the Renderer for drawing on the GLSurfaceView
renderer = GameRenderer().apply {
setRenderer(this)
}
}
inner class GameRenderer : Renderer {
override fun onSurfaceCreated(unused: GL10, config: EGLConfig) {
// Set the background frame color
GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f)
}
override fun onDrawFrame(unused: GL10) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
}
override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
}
}
}
In XML
<com.hiteshsahu.opengl.boilerplate.GameView
android:id="#+id/glView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
I need to create a circle that rotates and contains data for my application. Should I create a customized object for my application or should I make a in-application widget?
While on the topic, how do you refer to a widget within an application instead of a stand alone widget for the android desktop?
This is a rotatable LinearLayout that you can put everything in it and you can rotate it by degree if you customize it. use rotate() method to rotate it and...
enjoy! ;)
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class RotateLinearLayout extends LinearLayout {
private Matrix mForward = new Matrix();
private Matrix mReverse = new Matrix();
private float[] mTemp = new float[2];
private float degree = 0;
public RotateLinearLayout(Context context) {
super(context);
}
public RotateLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void dispatchDraw(Canvas canvas) {
try {
if (degree == 0) {
super.dispatchDraw(canvas);
return;
}
canvas.rotate(degree, getWidth() / 2, getHeight() / 2);
mForward = canvas.getMatrix();
mForward.invert(mReverse);
canvas.save();
canvas.setMatrix(mForward); // This is the matrix we need to use for
// proper positioning of touch events
super.dispatchDraw(canvas);
canvas.restore();
invalidate();
} catch (Exception e) {
}
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (degree == 0) {
return super.dispatchTouchEvent(event);
}
// final float[] temp = mTemp;
// temp[0] = event.getX();
// temp[1] = event.getY();
// mReverse.mapPoints(temp);
// event.setLocation(temp[0], temp[1]);
event.setLocation(getWidth() - event.getX(), getHeight() - event.getY());
return super.dispatchTouchEvent(event);
}
public void rotate() {
if (degree == 0) {
degree = 180;
} else {
degree = 0;
}
}
}
Update:
add this code to your xml layout and put your Views like ImageView or another LinearLayout in it :
<org.mabna.order.ui.RotateLinearLayout android:id="#+id/llParent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal" >
<ImageView
android:id="#+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:src="#drawable/main01" />
</org.mabna.order.ui.RotateLinearLayout>
in onCreate() method:
llParent = (RotateLinearLayout) this.findViewById(R.id.llParent);
in onClickListener of a button:
protected void btnRotate_onClick() {
llParent.rotate();
}
Update2:
You can use an animation for rotation before real rotation (llParent.rotate();). it needs an animation layout like rotate_dialog.xml:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000" android:fromDegrees="-180" android:toDegrees="0"
android:pivotX="50%" android:pivotY="50%" android:fillAfter="true" />
and in your code:
protected void btnRotate_onClick() {
// rotate
Animation rotateAnimation = AnimationUtils.loadAnimation(this,
R.anim.rotate_dialog);
llParent.startAnimation(rotateAnimation);
llParent.rotate();
}
There is a fairly easy way to make a rotating animation from a custom widget derived from the View class. After the view is created and placed in your layout, you can call View.setAnimation(Animation) or View.startAnimation(Animation), supplying a RotateAnimation on the view to start it. Here is an example of a rotation animation defined in xml, that can be loaded from your activity with getResources().getAnimation(int).
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="float"
android:toDegrees="float"
android:pivotX="float"
android:pivotY="float" />
I have created a 3D flip of a view using this android tutorial
However, I have done it programmatically and I would like to do it all in xml, if possible. I am not talking about simply shrinking a view to the middle and then back out, but an actual 3D flip.
Is this possible via xml?
Here is the answer, though it only works with 3.0 and above.
1) Create a new resources folder called "animator".
2) Create a new .xml file which I will call "flipping". Use the following xml code:
<?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>
No, the objectAnimator tags do not start with an uppercase "O".
3) Start the animation with the following code:
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();
I got all this from here.
Since the answers to this question are fairly dated, here is a more modern solution relying on ValueAnimators.
This solution implements a true, visually appealing 3D-flip, because it not just flips the view, but also scales it while it is flipping (this is how Apple does it).
First we set up the ValueAnimator:
mFlipAnimator = ValueAnimator.ofFloat(0f, 1f);
mFlipAnimator.addUpdateListener(new FlipListener(frontView, backView));
And the corresponding update listener:
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);
}
}
That's it!
After this setup you can flip the views by calling
mFlipAnimator.start();
and reverse the flip by calling
mFlipAnimator.reverse();
If you want to check if the view is flipped, implement and call this function:
private boolean isFlipped() {
return mFlipAnimator.getAnimatedFraction() == 1;
}
You can also check if the view is currently flipping by implementing this method:
private boolean isFlipping() {
final float currentValue = mFlipAnimator.getAnimatedFraction();
return (currentValue < 1 && currentValue > 0);
}
You can combine the above functions to implement a nice function to toggle the flip, depending on if it is flipped or not:
private void toggleFlip() {
if(isFlipped()){
mFlipAnimator.reverse();
} else {
mFlipAnimator.start();
}
}
That's it! Simple and easy. Enjoy!
I have created a simple program for creating flip of view like :
In Activity you have to create this method, for adding flip_rotation in view.
private void applyRotation(View view)
{
final Flip3dAnimation rotation = new Flip3dAnimation(view);
rotation.applyPropertiesInRotation();
view.startAnimation(rotation);
}
for this, you have to copy main class used to provide flip_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);
}
}
The tutorial or the link by om252345 don't produce believable 3D flips. A simple rotation on the y-axis isn't what's done in iOS. The zoom effect is also needed to create that nice flip feel. For that, take a look at this example.
There is also a video here.
One of the better solution to flip the image with out use of the resource animation , is as follow:-
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();
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
If you want to figure out how to create 3d flick animation, please follow here and here
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);
}
A full code snippet you can find here
Just put the view which you're going to animate it in place of viewToFlip.
ObjectAnimator flip = ObjectAnimator.ofFloat(viewToFlip, "rotationY", 0f, 360f); // or rotationX
flip.setDuration(2000); // 2 seconds
flip.start();
Adding to A. Steenbergen's great answer. When flipping the same view (updating a TextView for example) I removed the View.Visibility change in the constructor in order to keep the transition smoother.
public FlipListener(final View front, final View back) {
this.mFrontView = front;
this.mBackView = back;
}