Tags

, , , , , , ,

As mentioned in my previous post on Kinetic List View, here’s a way to get the rotation for views on pre-honeycomb devices. The RotateAnimation provided by Android rotates the view in an X-Y plane. However, the desired effect should be a 3-D space. To achieve this, Android allows us to append a matrix to the Canvas on which the View is drawn. But how do we calculate the matrix? An easy approach is to simulate the user with a Camera.

To achieve the same effect as setRotationX(), we need to rotate the Camera in X direction. The camera is by default placed at (0, 0, -8). We could actually change the location of the camera to achieve different effects. One catch here is that, every time we do a transformation, we need to move the origin to the center of the view and then apply the transformation about the center. The last step is to obtain the matrix from the camera and feed it to the canvas.

public class ListItem extends LinearLayout {
    private Camera mCamera;
    private Matrix mMatrix;

    public ListItem(Context context, AttributeSet attrs) {
        super(context, attrs);
        mMatrix = new Matrix();
		
        // Save it so that we know the initial camera setting.
        mCamera = new Camera();
        mCamera.save();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // Do the default draw.
        super.onDraw(canvas);
		
        // Translate the origin.
        int centerX = (canvas.getWidth() / 2);
        int centerY = (canvas.getHeight() / 2);
        canvas.translate(centerX, centerY);

        // Append the rotation from the camera.
        mCamera.getMatrix(mMatrix);
        canvas.concat(mMatrix);
		
        // Translate back
        canvas.translate(centerX * -1, centerY * -1);
    }

    // This method will be called by the ListView.	
    public void tilt(float deg) {
        // Every camera rotation should be from initial setting, 
        // hence restore and save for future.
        mCamera.restore();
        mCamera.save();

        // Rotate the camera.
        mCamera.rotateX(deg);

        // Re-draw the view for the tilt.
        invalidate();
    }
}

Notes:

  1. Android doesn’t recommend creating objects inside onDraw(). Hence, it’s better to instantiate the Camera and the Matrix in the constructor.
  2. It’s better to use setRotationX() in post-Honeycomb devices. This takes advantage of hardware acceleration. The above code is only for obtaining parity on pre-Honeycomb devices.