How to make Rotate3dAnimation more smoother?
One option for (non-OpenGL) 3d animation effects on Android is to implement the animations in a ViewGroup's getChildStaticTransform
method using the graphics.Camera
and Matrix
classes.
In broad terms it's done like this:
Extend ViewGroup or a subclass thereof.
In the constructors, set staticTransformationEnabled to true:
setStaticTransformationsEnabled(true);
Override the protected method getChildStaticTransformation(View view, Transformation t).
In getChildStaticTransformation, use
graphics.Camera
to rotate theView
as per your picture.Get the camera's matrix and adjust it to center the camera position on the view.
For example, this is how a 3d translation effect is done in the 3d carousel by Igor Kushnarev:
protected boolean getChildStaticTransformation(View child, Transformation transformation) {
//...
// Center of the item
float centerX = (float)child.getWidth()/2, centerY = (float)child.getHeight()/2;
// Save camera
mCamera.save();
// Translate the item to it's coordinates
final Matrix matrix = transformation.getMatrix();
mCamera.translate(((CarouselImageView)child).getX(),
((CarouselImageView)child).getY(),
((CarouselImageView)child).getZ());
// Get the camera's matric and position the item
mCamera.getMatrix(matrix);
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
// Restore camera
mCamera.restore();
return true;
}
Here are some more code examples on how to use graphics.Camera
and Matrix
in getChildStaticTransformation
:
ViewPager3d by Inovex. This project is interesting because if you run it as is, the 3d animation is not smooth (on a Galaxy S2). Hoverever, if you strip it of the non-camera/matrix overscroll animations but keep the getChildStaticTransformation 3d effects done with camera and matrix, the 3d effects are smooth.
CoverFlow by Neil Davies.
I will just give a small hint; however right now I am so busy at work I can not implement this.
The steps are
- get your drawing cache Bitmap
- set your content to an imageview only with this Bitmap
- apply the animation to this imageview
- at the end of the animation re set your content
This I believe will maximize the performance.
I will try to write some code later.
CODE
View longLivingReference; //keep a reference
private void applyRotation(int position, float start, float end) {
longLivingReference = findViewById(R.id.event_container);
longLivingReference .setDrawingCacheEnabled(true);
Bitmap bitmapForAnimation = Bitmap.createBitmap(longLivingReference.getDrawingCache());
ImageView iv = new ImageView(mContext);
iv = new ImageView(mContext);
iv.setImageBitmap(bitmapForAnimation);
setContentView(iv);
final float centerX = mContainer.getWidth() / 2.0f;
final float centerY = mContainer.getHeight() / 2.0f;
final Rotate3dAnimation rotation = new Rotate3dAnimation(start, end, centerX, centerY, 310.0f, true);
rotation.setDuration(500);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
rotation.setAnimationListener(yourAnimationListener {
//whatever your AnimationListener is, you can call super.onAnimationEnd if needed
@Override
public void onAnimationEnd(Animation animation) {
setContentView(longLivingReference);
}
});
iv.startAnimation(rotation);
}
I have made the animation like this. I had the same problems. So, suggestions:
make xml layout as simple as possible, you can test it using Hierarchy View tool in android. It tool shows the time of building and drawing the laoyuts;
images on layout should have so low weight as possible;
use hardware acceleration if your device supports it (in manifest):
I have noticed one more interesing behavior. If I call some code in onAnimationEnd(Animation animation) method, the animation freezes for a short time. This problem I was solved using next construction:
private static final int DELAY_AFTER_ANIMATION = 10;
public void onAnimationEnd(Animation animation) { new Handler().postDelayed(new Runnable() { @Override public void run() { setData(); // do the heavy code here } }, DELAY_AFTER_ANIMATION); }
To construct animation I used the same code (Rotate3dAnimation). For calling animation(main difference is using the isReverse parameter):
public void apply3dRotation(float start, float end, AnimationListener listener, boolean isReverse) {
View view = getRotateView();
if(view == null){
return;
}
if (isHardwareAcceleartionNotSupported()){
AndroidHelper.disableHardwareAccelerationOnView(view, this.getClass());
}
final float centerX = view.getWidth() / 2.0f;
final float centerY = view.getHeight() / 2.0f;
Flip3dAnimation rotation;
rotation = new Flip3dAnimation(start, end, centerX, centerY, 310.0f, isReverse);
rotation.setDuration(ANIMATION_DURATION);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
if(listener != null){
rotation.setAnimationListener(listener);
}
view.startAnimation(rotation);
}
isHardwareAcceleartionNotSupported() method checks the OS version. In my project I disabled acceleration for smartphones. In AndroidHelper class:
public static void disableHardwareAccelerationOnView(View view, Class c){
try {
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
} catch (Error e) {
Log.i(c.getSimpleName(), e.getMessage());
}
}
And one problem yet. If the animation hides when screen rotates to 90 degrees, it's a problem of a camera. In this case we should to place image farther from the spectator.
And one more trick - sometimes I set the pause before animation start:
animation.setStartOffset(100);