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 the View 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 Matrixin 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);