Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw onto a view canvas without upscaling artifacts when the view parent has a scale factor?

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:

Button with parent unscaledButton with parent scaled (5x)

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.

like image 204
Lars Blumberg Avatar asked May 19 '14 14:05

Lars Blumberg


People also ask

How do you scale drawings up?

I typically only use my printer to scale drawings up when the final drawing needs to be no larger than 8-1/2 by 14 inches (a legal sized sheet of paper.) For anything larger, I use a copy machine or print shop. 4. The projector method Projectors are an excellent way to scale drawings up.

What is the use of scale in sheet View?

This scale is also used in any sheet views generated from the view. Layer changes (including xref dependent layer changes) from the view drawing can be displayed and updated in the sheet view, if the project has been set up to synchronize sheets with views.

How to insert a view drawing in AutoCAD?

Select the view drawing you want to place on the sheet, and drag it from the Drawing Explorer to the drawing area of the sheet. Tip: While inserting the view drawing on a sheet, you can change the scale of the resulting sheet view by right-clicking and selecting a different scale before defining the insertion point.

How to change the scale of sheet View in AutoCAD?

Tip: While inserting the view drawing on a sheet, you can change the scale of the resulting sheet view by right-clicking and selecting a different scale before defining the insertion point. After creating the sheet view, you can change its scale on the Properties palette.


1 Answers

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.

  • My favorite solution that I'm using pretty oftenDetach your 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.
  • Override 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.
  • Vice versa. In your 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.
like image 100
Dmitry Zaytsev Avatar answered Sep 24 '22 01:09

Dmitry Zaytsev