The Bit Anvil

Adroit development.

Before I forget - a couple of things about android app development that caused me some trouble recently.

Those being, that the canvas in a view doesn't have a  identity matrix as default!

What now?, I hear everyone cry?

Before I explain let me say that I was developing against API 8, you know the one that supports gramophones - but I can't think that this particular feature has changed.

It goes like this. You have a View on which to display some things. Inside of their view, or rather attached to it you have a Canvas.  All good so far. You can draw on the canvas. Right? However, have you ever tried to centre something in the canvas using canvas.getHeight() / 2 and have it not be in the middle? But then use this.getHeight() / 2 and have it work?

You then realise that the canvas is not the same size as the view, and go off and read the Google API docs And various stack overflow thingies about your observation?

Here is what is going on with this (and more importantly high light a gotcha)

The View-canvas contains a matrix (as all canvases do.) But you will notice that your view also has a status bar at the top (usually) and if you do mathematics on this.getHeight() - canvas.getHeight() it will come out as the hight of the status bar?

However, the canvas actually starts underneath the status bar. But when you put a pixel at 0,0 on the canvas it comes under the status bar i.e. in the right place? What's going on?

Well turns out there is a default matrix on the canvas that translates all your draws by the size of the status bar!

Oh! Neat huh?

Not really, if you ever try and do any matrix operations on the canvas, but don't realise it has a non identify matrix you will likely learn your transformations don't quite work. For example:

Translate to origin, followed by rotate, then translate to centre. No worky.

There are a few workarounds for this. One being a copy of the default matrix, and add it to the end of your matrix stack. Or - use a new canvas and blit that to the canvas you are given with the view.

This discovery, needles to say wasn't my favourite since it isn't documented anywhere in the android docs. Unless I missed it. I'd rather you not see this magic matrix transform in your view. Would have been better if it were done in a parent view. But I guess the way they designed the thing their isn't one to hand, hence this magic.

Oh, and the other thing is that the ScaleGestureDetector implementation in android 2.3 is broken. Essentially you end up with crazy horse scaling if you take your fingers off at the edge of the screen and stuff. A web search will yield you some fixes - one which I found not to work. The other is to wrap the call to the scale listener in and if statement to convince it to only do stuff when two pointers are down.

The fix that worked for me is here: http://code.google.com/p/android/issues/detail?id=12976

Comments