Reputation: 21371
When drawing onto a canvas in a View
's onDraw
method you don't know about any scale factor of its parent. This leads to all views and their drawings being up/down scaled whereby one can explore "pixelized" drawings.
Example: So let's say we've got a child view with a custom drawing
class MyView extends View {
@Override
public onDraw(Canvas canvas) {
// Draw something fancy here that looks ugly when scaled up
}
}
And we add this this view into a parent view:
MyView view = new MyView(this);
parent.addChild(view);
parent.setScaleX(10);
parent.setScaleY(10);
The canvas drawing in the subview will thus being upscaled by factor 10. This is due to onDraw()
providing a canvas to draw to that does not reflect this scale factor. That said the canvas always keeps the same dimensions regardless of its final destination. Thus child views do not have the chance to provide higher resolution drawings when their parents are being scaled up. This obviously leads to scaling artifacts.
You can observe the same behavior when you scale a button's parent view:
Is there any chance to respect all parents transformations when drawing onto child views? Or is this a general limitation when drawing onto canvases in a view hierarchy where view parents can have a scale factor not equal to 1.
In my use case it is not an option to forward the scale change to all children instead of applying it to the parent. This is due to the fact that there some of those children have animations that modify the scale factor. Hence these animations would override any scale change.
Upvotes: 8
Views: 1158
Reputation: 23952
This issue takes it roots from Composition pattern. Android View Framework (that's not an official name, but you got the idea) also follows this pattern - ViewGroup
(base class for all layouts) is essentially a View
. So, when your layout draws itself it just takes content of all children views. Sounds easy.
Since API Level 11 View
transformation matrix can be modified via setScaleX
, setTranslationX
, etc. methods (well, in fact it was possible before API Level 11, but it was a bit tricky). This transformation is then used to transform drawings of this particular View
. Therefore, when you're applying transformation to ViewGroup
, you're affecting only ViewGroup
drawing itself, which in return consists of drawing its children.
Moreover, those transformations are affecting only drawing and dispatch of motion events. So, when you're applying setScale*
you're just modifying the visual representation of the View
, while logical coordinates and size of the View
stays the same (getLeft
, getTop
, getRight
, getBottom
, getWidth
, getHeight
).
Now closer to your question. In short - you're doing it wrong. More detailed explanation follows from my previous statements. By affecting transformation of View
you're not changing it's logical position and size. Therefore child View
won't detect anything abnormal while drawing - it draws as if there was no scale at all.
What to do then? I would propose three different solutions.
View
from layout being scaled and add it on top of it. Then you'll need to scale this View
manually, taking into account data behind it.setScale
method of layout being scaled to propagate it to children. You can create some interface and check if any of the children implements it.View
iterate through the all parents until you'll find the root and then calculate scale accordingly. This shouldn't be to much work if your hierarchy is not too deep.Upvotes: 2