Smooth scrolling on Android with slow onDraw - android

I have a big custom View, and I draw a lot of rich text on it, many fonts, multiple colors and font sizes. I already optimized this drawing as much as possible. The redraw takes about 300ms.
This 300ms is OK for one time call, but since my View is bigger than the screen, it is inserted into the ScrollView, and now once the user scrolls with the finger, onDraw() is called repeatedly. Which gives very sloppy scrolling.
The same application was previously written for iOS, and iOS will not call onDraw (I think it is called drawRect() in iOS world), and all the drawn image is stored in some sort of buffer, so scrolling actually is very fast and ultra smooth, probably hardware accelerated.
So is there a way to achieve something like "No onDraw" smooth scrolling? I was thinking about drawing onto bitmap, and then blitting the bitmap during the scroll phases. But will it be any faster than drawing text? Are bitmaps HW accelerated on Android? What is the best approach to this kind of problem?
Thank you

Make the entire drawing into a Bitmap object using associated Canvas and in onDraw just flush this Bitmap. When you data changes, redraw everything into Bitmap.

Related

Finger drawing application with unlimited scroll

I want to make finger-draw app with vertical scroll and possibly unlimited space to draw. Scroll can be locked/unlocked.
I know 2 ways to do that:
Make a bitmap with given width and height, draw points/lines in onTouchEvent(MotionEvent) via Canvas(Bitmap) and draw that bitmap in onDraw(Canvas).
Save touch points to array and draw them directly in onDraw(Canvas).
But both ways have disadvantages:
OutOfMemoryException with big bitmap.
Very slow with numerous points while scrolling and OOM possible too.
I have an idea to use window and read/write regions of bitmap on demand. So there is a problem: how to write/append bitmap to another bitmap?
I cant find an appropriate way to append 200x200px bitmap to 8000x8000px bitmap for example. Is there a way to do that without native libs?
Or am I totally wrong and there is an easier method to do what I need to?
What about using a grid of small bitmaps, without appending them to a larger bitmap at all? When one of your bitmaps goes out of the screen you save it to the disk and destroy it or recycle it, a bit like how GridView recycles cells (it reuses the same 10 cells or so to display a potentially infinite grid of cells). Saving the whole drawing would just mean saving all bitmaps to the disk.
The main concern with this approach is the time it takes to save/load chunks from disk while the user scrolls through the view, which may cause the app to freeze for a short time, but there are ways to make it smooth (for example by preloading bitmaps in a background thread).
Storing touch points in a list is also completely possible, there are many ways to speed up the drawing part, for example by caching what's on the screen and drawing only what's missing when the user scrolls. Which one is best depends on what you will draw on your view.

android: Smoothest and most efficient way to animate drawn circles on a canvas

What is the best way to draw circles on a canvas that should have an alpha layer and change sizes? Should I use a View or a Surfaceview? The circles should also be clickable. And it should be smooth transitions when changing color size and position?
Should I put this in a runnable or use invlaidate in onDraw?
I would prefer that something like this also worked smoothly in low-end devices.
Any suggestions? I'm new to this kind of animations in Android.
If you are constantly drawing and taking user input at the same time, I would use a SurfaceView. However, if the only draw changes you plan on making to the circles happen when you touch them, then a simple View onDraw() override would probably do the trick. In the end it will just depend on what all is going on.
The point of the SurfaceView is to have that separate thread for drawing. If what you're doing could be in any way considered "game-like," then go for a SurfaceView; otherwise, stick with a View.
I say this because I'm currently working on a project with constant drawing using a View. The shapes that I'm drawing respond to touch and you can scroll through the View while it is still invalidating over and over. All this with a View and it still runs just fine on lower-end devices (I've only gone back to GingerBread, though).
Good luck!
I should also mention that in the project drawing in a View, almost everything has various alpha values and what not and runs fine.

android canvas - can i have multiple draw methods?

I have made my own canvas class which extends an imageView. My onDraw() method draws out the users gps position and I keep calling this onDraw method every time the user moves. My problem is I also want to draw out a gps trail which only needs to be drawn once (doesnt need to be updated when a user moves). I am wondering is it possible to have more than 1 onDraw method or is there any way of separating 1) the user location and 2) the gps trail??
My reason is I do not want to waste memory by redrawing the gps route everytime the users gps position changes. It is a waste.
Have you seen performance take a hit? If not, don't worry about it. I would think that this would be wasting CPU cycles if anything... not memory. So if the app seems fast enough already, don't worry about optimizing it.
If your app is a bit laggy, and you've found that the trail is the bottleneck... I would suggest caching it into a bitmap. This way, you will still have to draw the trail, but you will not have to calculate the coordinates of the trail on each frame.
I have had to solve a somewhat similar problem recently and I'll explain briefly what I did in case it's of any help.
What you can do is use multiple overlapping Views, where one may contain the background graphics that you don't want to redraw often, and a foreground View that contains the graphics that are frequently updated. Then, to gain performance, you can design the background View's onDraw() so that it is backed by a Bitmap that you then retain as a class variable. In the very first onDraw() of your background graphics, you do the relatively slow drawing to Canvas. In subsequent calls to onDraw(), you simply draw that Bitmap to Canvas.
I've just done this myself. Basically what my application does is display a number of graphical gauges. Those gauges have lots of graphics that are drawn just once (gauge face, number legends), and the pointer graphic that needs to be redrawn over and over as the model data changes. First of all, I split the background graphics and moving foreground graphics into separate overlapping Views. Now, invalidating and redrawing the foreground pointer graphic of course causes anything it overlaps to be invalidated too, so the onDraw() method for the background graphics View is being called each time the pointer View is redrawn. The background View only needs to draw the background graphics once, but retains the Bitmap that backs the canvas, and in subsequent onDraw() calls it draws this bitmap back to Canvas (which is a lot faster than initially creating the graphics using Path() objects).
Bitmap bm;
....
protected void onDraw(Canvas canvas){
if(null==bm){
bm=Bitmap.createBitmap(getMeasuredWidth(),getMeasuredHeight(),Bitmap.Config.ARGB_8888);
// do your slow vector graphics drawing to Canvas here
}
Paint drawPaint = new Paint();
drawPaint.setAntiAlias(false);
drawPaint.setFilterBitmap(false);
drawPaint.setDither(false);
canvas.drawBitmap(bm, 0, 0, drawPaint);
}
Well, there can't be more than 1 onDraw method, assuming that I understood your question correctly. You will need to think about alternate approaches about how to handle this.
#DeeV suggested, that can be a solution for you.

GridView with images vs custom drawn Canvas for Android memory game

Say I'd like to make a memory/pairs game. I have currently made a draft that works on a Canvas, and cards are drawn into a grid.
This works for my current basic version, but I'd like show do an animation (when the card is turned, it will flip around and scale to higher size; or when the match is found, the cards would rotate around and then go back.
I can't imagine doing this on Canvas, I'd have to make a lot of timers and do the animation by hand, it seems overly complex for this simple task.
I think I could could subclass View for a control that would display a card, and then react to touch events for that control. It would also make drawing scaling of the images done by Android itself, and, most importantly, I could use Tween Animation for some effects.
My question is - would it be OK to use a View for each card in the game (I could have 5x6 or 4x5 cards), and arrange them in a GridView? Are there some pitfalls with this approach? Or should I continue with completely custom-drawn Canvas?
For such a simple game you should be fine using a collection of Views. As you mention using Views rather than trying to do it manually you get access to a lot of nice Animation functionality for free.
It also makes implement the user interface a lot simpler as you can just add onClickListeners to each view to capture user touches. If you're drawing it all manually to a Canvas then you'd have to interpret the touches yourself and decide which card was touched etc. While this isn't too hard, then I think subclassing View is a better model and will most likely result in cleaner code.
As you are only going to have 30 cards, then I can't imagine you having performance issues either - if you were thinking 100+, then maybe you'd have an issue, but I think you're fine. Also, if I understand your game correctly, the majority of your cards won't be animating most of the time so that's yet another reason not to worry - if you ever run into performance issues with the animations you can easily save off all the unanimated Views onto a Bitmap (Canvas) for the duration of the animation.

Android scrolling background of bitmap tiles

I'm tried to determine the "best" way to scroll a background comprised of tiled Bitmaps on an Android SurfaceView. I've actually been successful in doing so, but wanted to determine if there is a more efficient technique, or if my technique might not work on all Android phones.
Basically, I create a new, mutable Bitmap to be slightly larger than the dimensions of my SurfaceView. Specifically, my Bitmap accomodates an extra line of tiles on the top, bottom, left, and right. I create a canvas around my new bitmap, and draw my bitmap tiles to it. Then, I can scroll up to a tile in any direction simply by drawing a "Surfaceview-sized" subset of my background Bitmap to the SurfaceHolder's canvas.
My questions are:
Is there a better bit blit technique than drawing a background bitmap to the canvas of my SurfaceHolder?
What is the best course of action when I scroll to the edge of my background bitmap, and wish to shift the map one tile length?
As I see it, my options are to:
a. Redraw all the tiles in my background individually, shifted a tile length in one direction. (This strikes me as being inefficient, as it would entail many small Bitmap draws).
b. Simply make the background bitmap so large that it will encompass the entire scrolling world. (This could require an extremely large bitmap, yet it would only need to be created once.)
c. Copy the background bitmap, draw it onto itself but shifted a tile length in the direction we are scrolling, and draw the newly revealed row or column of tiles with a few individual bitmap draws. (Here I am making the assumption that one large bitmap draw is more efficient than multiple small ones covering the same expanse.)
Thank you for reading all this, and I would be most grateful for any advice.
I originally used a similar technique to you in my 'Box Fox' platformer game and RTS, but found it caused quite noticeable delays if you scroll enough that the bitmap needs to be redrawn.
My current method these games is similar to your Option C. I draw my tiled map layers onto a grid of big bitmaps (about 7x7) taking up an area larger than the screen. When the user scrolls onto the edge of this grid, I shift all the bitmaps in the grid over (moving the end bitmaps to the front), change the offset of grid, and then just redraw the new edge.
I'm not quite sure which is faster with software rendering (your Option C or my current method). I think my method maybe faster if you ever change to OpenGL rendering as you wouldn't have to upload as much texture data to the graphics card as the user scrolls.
I wouldn't recommend Option A because, as you suggest, the hundreds small bitmap draws for a tiled map kills performance, and it gets pretty bad with larger screens. Option B may not even be possible with many devices, as it's quite easy to get a 'bitmap size exceeds VM budget' error as the heap space limit is set quite low on many phones.
Also if you don't need transparency on your map/background try to use RGB_565 bitmaps, as it's quite a lot faster to draw in software, and uses up less memory.
By the way, I get capped at 60fps on both my phone and 10" tablet in my RTS with the method above, rendered in software, and can scroll across the map smoothly. So you can definitely get some decent speed out of the android software renderer. I have a 2D OpenGL wrapper built for my game but haven't yet needed to switch to it.
My solution in a mapping app relies on a 2 level cache, first tile objects are created with a bitmap and a position, these are either stored on disk or in a Vector (synching is important for me, multithreaded HTTP comms all over the place).
When I need to draw the background I detect the visible area and get a list of all the tiles I need (this is heavily optimised as it gets called so often) then either pull the tiles from memory or load from disk. I get very reasonable performance even on slightly older phones and nice smooth scrolling with no hiccups.
As a caveat, I allow tiles not to be ready and swap them with a loading image, I don't know if this would work for you, but if you have all the tiles loaded in the APK you should be fine.
I think one efficent way to do this would be to use canvas.translate.
On the first drawing the entire canvas would have to be filled with tiles. New android phones can do this easily and quickly.
When the backround is scrolled I would use canvas.translate(scrollX, scrollY), then I would draw individualy one by one tile to fill the gaps, BUT, I would use
canvas.drawBitmap(tileImage[i], fromRect, toRect, null) which would only draw the parts of the tiles that are needed to be shown, by setting fromRect and toRect to correspond to scrollX and scrollY.
So all would be done by mathematics and no new bitmaps would be created for the background - save some memory.
EDIT:
However there is a problem using canvas.translate with surfaceView, because it is double buffered and canvas.translate will translate only one buffer but not the second one at the same time, so this alternating of buffers would have to be taken into account when depending on surfaceView to preserve the drawn image.
I am using your original method to draw a perspective scrolling background. I came up with this idea entirely by accident a few days ago while messing around with an easy technique to do a perspective scrolling star field simulation. The app can be found here: Aurora2D.apk
Just tilt your device or shake it to make the background scroll (excuse the 2 bouncing sprites - they are there to help me with an efficient method to display trails). Please let me know if you find a better way to do it, since I have coded several different methods over the years and this one seems to be superior. Simply mail me if you want to compare code.

Categories

Resources