I know this is quite the same question as partial-invalidation-in-custom-android-view-with-hardware-acceleration but I thought I will explain my problem more detailled and hope someone will have an answer on how to solve the problem.
I wrote a simple TicTacToe app which was working fine on the emulator. But wasn't working on the device since I found out it was working as expected on the device when I disabled hardware acceleration.
All the fields are drawn with Canvas.drawBitmap(), also the "empty" fields with an empty white image. The lines are drawn with Canvas.drawLine()
I'm using partial invalidation with invalidate(Rect) because I want to redraw only the area which was choosen to set a cross/circle with the according image using again Canvas.drawBitmap(). But then the whole area is invalidated, means I see the whole area/screen is gray (the white images and lines disappeared) and only the image for the cross/circle is set in the choosen area.
When I print out the invalidated field rect with Canvas.getClipBounds() in the method onDraw(), with hardware acceleration it is the whole area (example "0,0,320,407") and without hardware acceleration the same rect which I invalidated with invalidate(Rect) (e.g. "106,135,106,135").
At the moment as workaround I redraw all the fields whith the according image and lines. How can I prevent to have the whole area invalidated with hardware acceleration?
If this matters: I'm using Android version 4.1.2 on Samsung Galaxy Young Duos S6312.
Regards
Sandro
I have post my answer in your given link. To understand the problem , you should know the difference between software rendering and hardware rendering. That's really a big topic which I won't cover here. I think the best approach is to read the source code(I did the same thing a few days ago). The key question is "what is DisplayList and when it is used"
. Repeat my answer here, the Canvas in onDraw is simply used to rebuild the DisplayList. That doesn't mean everything in your View will be redrawn.
Related
I have an Android App that is custom drawing a View (full-screen). I want to invalidate a part of it, then just draw that part in the next onDraw() call. I want to do this in a cycle, drawing each part of the screen at a time. What I have found is that it works well on Nexus 7 (Android 4.4.2) not too bad on Galaxy Nexus (4.3) but never draws the whole screen at one time on HTC Sensation XL (4.0.3). Did some bugs get fixed with the pipeline rendering, or am I doing something wrong? -- What is the correct way to do this?
More detail: I have a set of Rect objects. There is an update method in a handler that regularly calls Invalidate(Rect). Each time, based on an index. in the onDraw() I use
canvas.drawBitmap(background, src, dest, null);
Where src and dest are the same Rect that were used for the invalidate call. At the end of the onDraw method, the index is updated. It appears that in Android 4.0.3 the whole canvas has been blanked, but in 4.3 and 4.4.2 it has not.
What am I really trying to achieve? Spend as little time as possible in each call to onDraw (< 1mS) so that the CPU is free to do other things, and at the same time maintain a UI with a number of bitmaps being drawn, rotated etc. This is most important on the older-less capable phones that are likely to be running 4.0.3
In the end I took a different approach as I could not find a direct way to resolve the problem. I have implemented an OpenGL surfaceView based on this tutorial. I put everything in one Texture (png file) and recycle the verticies, indicies and uvs arrays and associated direct Buffers. The result is that the onDrawFrame in the Renderer class is completing in about 0.4 mS every time even on slower phones. This is what I needed.
I needed days to figure out why invalidate(Rect dirty) doesn't set the correct clip on newer devices.
The problem is that hardwareAccelerated="true" is set by default.
When I set this hardwareAccelerated="false",
I get my right clip in my onDraw.
Now my question is:
What is better for performance:
Use hardwareAccelerated="false" and draw only the part of my view which is dirty and run only the code in my onDraw that is needed for this
OR
allow hardwareAcceleration and let android draw my entire view over and over again?
I guess this question may be answered for every application individually so I add a screenshot of my app.
As you can see I draw a calendar and each time a user clicks a entry this should get a red border.
Can you give me any suggestions whats best for this requirement?
Cheers,
Stefan
I have a GLSurfaceView with a renderer assigned to it. On this view, I draw images which seem to be offset to the left compared to where I have requested that they be drawn on it. This problem has only arisen since I switched from Canvas-based rendering, which rendered the images in their correct positions, to OpenGL 1.0 rendering, which doesn't.
I have read through my code and have found absolutely nothing that could be causing this. What frustrates me is that I know there is nothing wrong with the positions I set using my code, but the problem still persists.
Now, here's the best part of it all. When I pause the application by pressing the home button, and then restart the application through the task manager, the positions are fixed and are exactly how they should be, which leads me to believe that the problem isn't with the positions I set using my code, but how I've set up my surface view. I've checked the dimensions in the onSurfaceChanged() parameters only to find that they are always as they should be, which leaves me even more confused.
Does anyone have any idea what could be causing this problem?
After much digging through my project, I discovered I was setting positions based on the dimensions of the GLSurfaceView before onSurfaceChanged had been called in the onSurfaceCreated method, therefore the width/height of the view at this point was 0 which affected the positions of my objects on the screen.
I feel really stupid now.
For a drawing app, I have to make sure that the canvas is not bigger then the touchable area of the screen, cause otherwise I would have a padding on the canvas, where it is not possible for the user to draw on.
On my Galaxy S that's about 10 pixel I guess.
Is there a way to find out the exact number of pixels which are not touch-sensitive?
Sorry but that has no sense . I think that maybe components are not good enough , but the screen in android must have the capability to touch all window places .
If you have the reason and the producer make that horrible mistake , program thinking that you don't have the whole touchable area , you would make a bad thing for producers that do it well.
Anyway if you are thinking in usability for users , don't work with pixels , work with dip .
I've got a custom View in my application which fills the entire activity.
In most cases, when I want to refresh the control I call invalidate() without any parameters.
However, in certain circumstances, where I'm only changing a small area of the control, I call invalidate(Rect) to avoid redrawing the entire screen. This is important because in these situations I
need the update to be as fast as possible.
This seems to work fine, however, when I enable hardware acceleration in Honeycomb (i.e. set android:hardwareAccelerated="true"in AndroidManifest.xml), the partial redraw does not seem to work.
This can be seen if I do Log.d("FOO", canvas.getClipBounds()) in my onDraw() method; the output simply shows that the whole control is being updated, whereas with hardware acceleration disabled, I get the correct region being output.
Is there any way to make partial invalidation work when using hardware acceleraton?
Many thanks, Matt
Partial redraw works just fine, only the specified region of the screen will get redrawn. What it won't do however is change the clip bounds on the Canvas. All the drawing operations will be recorded but only the ones intersecting with the dirty region will actually be executed.
Update: as of Lollipop (API 21), partial invalidation happens at the View level (i.e. you cannot invalidate less than an entire View).
When HA is enabled, the rendering pipeline will use DisplayList to store the drawing commands. Even if you specify the dirty region in View.invalidate, the whole displaylist will be rebuilt (just think that how can we only update a small set of DisplayList, that's impossible right?). Eventually, as #Romain
says, only the real dirty region will be redrawn.