Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

View.invalidate() does not seem to call View.onDraw()

I have a drawing library, and with it I have a custom View. When any updates need to be done, the library calls the view's invalidate() method. And then the onDraw() method is called, and everything works just fine.

However, when I add the attribute of usesSdkVersion="14" to my AndroidManifest.xml file, this stops working. No longer is my view's onDraw() method getting called anymore.

MORE INFO: It seems that when I call invalidate() as the result of a button push in my app, the View updates as expected. Also, there is one animation inside my library which is working correctly. So apparently sometimes it is working correctly. But most of the time, it is not getting redrawn. Is there any state that a custom View can be in that would cause the OS to skip redrawing it after invalidate() is called?

like image 828
Rickster Avatar asked Apr 13 '12 21:04

Rickster


3 Answers

From android 3.0, when using Hardware acceleration, the drawing of views is recorded and stored at the GPU.

So when you got two views: A above B, and you call B.invalidate, B.onDraw will be called and record to the GPU, but A.onDraw will not be called because it's already recorded!

This is the different from non-hardware acceleration mode when all the views onDraw been called.

like image 90
Ilya Gazman Avatar answered Nov 06 '22 09:11

Ilya Gazman


by official documents:

However, because hardware acceleration is not supported for all of the 2D drawing operations, turning it on might affect some of your applications that use custom views or drawing calls. Problems usually manifest themselves as invisible elements, exceptions, or wrongly rendered pixels.

To remedy this, Android gives you the option to enable or disable hardware acceleration at the following levels:

Application

<application android:hardwareAccelerated="true" ...>

Activity

<application android:hardwareAccelerated="true">
  <activity ... />
  <activity android:hardwareAccelerated="false" />
</application>

Window

getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

View

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

I test in my view.

like image 37
tiancao222 Avatar answered Nov 06 '22 10:11

tiancao222


It seems like a bug in Android. Instead of invalidate() try to use method of the same name with four parameters:

view.invalidate(0,0,view.getWidth(), view.getHeight());

By the way, it is better practice to invalidate only that region, which is changing. Pass left, top, right, bottom coordinates of invalidated region.

like image 2
AterLux Avatar answered Nov 06 '22 11:11

AterLux