Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas not displaying all drawn parts in Custom View?

I'm working on a custom view for an android application, similar to the Analog Gauge sample code available from Mind the Robot.

Running the code from listed site, I get see this on my screen:

(Motorola Droid, 2.2.3), (Emulator, 4.0.3)

has an arrow

(Xoom, 4.0.3)(Other phone, 4.0.3)

does not have an arrow

The hand is missing!

The drawing calls are being made (I can see them in logcat), but the canvas elements the calls draw are invisible.

It's not API level dependent, though; if I import it the right way into a project, it will hand will show up when I run it on the Xoom.

But, when I move the files to a different project folder (same source code, same layouts) it goes back to missing the dial.

What's going on? How could the same code be producing such different outcomes on different devices?

like image 336
Eagle Avatar asked Jul 12 '12 21:07

Eagle


3 Answers

So, the key clue in my mystery seemed to be that it worked on the emulator, but not on the hardware devices.

Hardware Rendering

I did peruse the hardware rendering page on the Android Developer's website, but apparently not closely enough.

http://developer.android.com/guide/topics/graphics/hardware-accel.html

While it does mention that the API's are available beginning version 11, it does not say that Hardware Rendering is turned on for all applications by default, starting with API Level 14 (ICS).

What does this mean for us?

Almost everything is faster; except for the few things that don't work.

I managed to violate two of these, without realizing it:

  • Canvas.DrawTextOnPath()
  • Paint.setShadowLayer()

It's not mentioned in the API reference (or anywhere else I can find, and certainly not checked by Lint), but using any of the listed operations can do weird things.

In my case, Canvas.DrawTextOnPath() seemed to work just fine.

But when Android notice that the paint that I used on the hand had shadow layer set, it silently ignored it.

How do I know if my View is hardware accelerated?

From the documentation link above:

There are two different ways to check whether the application is hardware accelerated:

  • View.isHardwareAccelerated() returns true if the View is attached to a hardware accelerated window.
  • Canvas.isHardwareAccelerated() returns true if the Canvas is hardware accelerated

If you must do this check in your drawing code, use Canvas.isHardwareAccelerated() instead >of View.isHardwareAccelerated() when possible. When a view is attached to a hardware >accelerated window, it can still be drawn using a non-hardware accelerated Canvas. This >happens, for instance, when drawing a view into a bitmap for caching purposes.

In my case, the opposite appears to have occurred. The custom view logs that it is not Hardware-accelerated; however, the canvas reports that it is hardware-accelerated.

Work Arounds and Fixings

The simplest fix is forcing the custom view to do software rendering. Per the documentation this can be accomplished by:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

Alternatively, you could remove the offending operations, and keep hardware rendering turned on.

Learn from my misfortune. Good luck, all.

like image 80
Eagle Avatar answered Oct 22 '22 20:10

Eagle


I put it into init() and worked fine after that.

    private void init() {
    setLayerType(myView.LAYER_TYPE_SOFTWARE, null);
    ....
}
like image 42
JokkeH Avatar answered Oct 22 '22 20:10

JokkeH


With myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); suggestion I can see hand. But I have still a problem: I see scale with only 0 written! As in the picture and two strage zeros out of the schema: (GALAXY NEXUS 4.2.1)

enter image description here

My drawScale() method is as in the example:

 private void drawScale(Canvas canvas) {
        canvas.drawOval(scaleRect, scalePaint);

        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        for (int i = 0; i < totalNicks; ++i) {
            float y1 = scaleRect.top;
            float y2 = y1 - 0.020f;

            canvas.drawLine(0.5f, y1, 0.5f, y2, scalePaint);


            if ((i % 5) == 0) {
                int value = nickToDegree(i);

                if ((value >= minDegrees) && (value <= maxDegrees)) {
                    String valueString = Integer.toString(value);
                    canvas.drawText(valueString, 0.5f, y2 - 0.015f, scalePaint);

                }
            }

            canvas.rotate(degreesPerNick, 0.5f, 0.5f);


        }
        canvas.restore();

    }
like image 43
Domenico Pacecca Avatar answered Oct 22 '22 22:10

Domenico Pacecca