views:

2050

answers:

3

I'm new to Android.

I am drawing bitmaps, lines and shapes onto a Canvas inside the OnDraw(Canvas canvas) method of my view. I am looking for help on how to implement smooth scrolling in response to a drag by the user. I have searched but not found any tutorials to help me with this.

The reference for Canvas seems to say that if a Canvas is constructed from a Bitmap (called bmpBuffer, say) then anything drawn on the Canvas is also drawn on bmpBuffer. Would it be possible to use bmpBuffer to implement a scroll ... perhaps copy it back to the Canvas shifted by a few pixels at a time? But if I use Canvas.drawBitmap to draw bmpBuffer back to Canvas shifted by a few pixels, won't bmpBuffer be corrupted? Perhaps, therefore, I should copy bmpBuffer to bmpBuffer2 then draw bmpBuffer2 back to the Canvas.

A more straightforward approach would be to draw the lines, shapes, etc. straight into a buffer Bitmap then draw that buffer (with a shift) onto the Canvas but so far as I can see the various methods: drawLine(), drawShape() and so on are not available for drawing to a Bitmap ... only to a Canvas.

Could I have 2 Canvases? One of which would be constructed from the buffer bitmap and used simply for plotting the lines, shapes, etc. and then the buffer bitmap would be drawn onto the other Canvas for display in the View?

I should welcome any advice!

Answers to similar questions here (and on other websites) refer to "blitting". I understand the concept but can't find anything about "blit" or "bitblt" in the Android documentation. Are Canvas.drawBitmap and Bitmap.Copy Android's equivalents?

+4  A: 

I seem to have found an answer. I have put the bulk of the drawing code (which was previously in onDraw()) in a new doDrawing() method. This method starts by creating a new bitmap larger than the screen (large enough to hold the complete drawing). It then creates a second Canvas on which to do the detailed drawing:

    BufferBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
    Canvas BufferCanvas = new Canvas(BufferBitmap);

The rest of the doDrawing() method is taken up with detailed drawing to BufferCanvas.

The entire onDraw() method now reads as follows:

    @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(BufferBitmap, (float) -posX, (float) -posY, null);
}

The position variables, posX and posY, are initialised at 0 in the application's onCreate()method. The application implements OnGestureListener and uses the distanceX and distanceY arguments returned in the OnScroll notification to increment posX and posY.

That seems to be about all that's needed to implement smooth scrolling. Or am I over-looking something!?

One thing that I have discovered since writing this "answer" is that extra code is needed to "recycle" the buffer bitmap (and any other bitmap objects) when the screen is rotated. This is because when the screen is rotated the app's main activity is ended and restarted but the memory used by Bitmap objects is not automatically recovered by the system. So, it seems to be important to call the recycle() method for every bitmap when it is no longer needed. In the case of the buffer bitmap the answer seems to be to override the Activity's onDestroy method and to call the bitmap's recycle() in it.
A: 

prepbgg: I don't think the code will work because canvas.drawBitmap does not draw into the bitmap but draws the bitmap on-to the canvas.

Correct me if I am wrong!

MasterGaurav
Thanks, MasterGaurav. I agree that canvas.drawBitmap(BufferBitmap, ...) does not draw anything into BufferBitmap. However, I have a separate doDrawing() method where things (lines, bitmaps, etc.) are drawn into BufferCanvas and thereby into BufferBitmap.The code does seem to work (although it does occasionally stutter and/or crash ... I don't know whether that is because my drawing code is faulty or whether there are other errors.)
A: 

I had this problem too,

I did the drawing like this:

Canvas BigCanvas = new Canvas();
Bitmap BigBitmap = new Bitmap(width,height);

int ScrollPosX , ScrollPosY  // (calculate these with the onScrollEvent handler)

void onCreate()
{
   BigCanvas.SetBitmap(BigBitmap);
}

onDraw(Canvas TargetCanvas)
{
   // do drawing stuff
   // ie.  BigCanvas.Draw.... line/bitmap/anything

   //draw to the screen with the scrolloffset

   //drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint)
   TargetCanvas.DrawBitmap(BigBitmap(new Rect(ScrollPosX,ScrollPosY,ScrollPosX + BigBitmap.getWidth(),ScrollPosY + BigBitmap.getHeight(),new Rect(0,0,ScreenWidth,ScreenHeight),null);
}

for smooth scrolling you'd need to make some sort of method that takes a few points after scrolling (i.e the first scroll point and the 10th) , subtract those and scroll by that number in a for each loop that makes it gradually slower ( ScrollAmount - turns - Friction ).

I Hope this gives some more insight.

Mervin
Thanks, Mervin, for your reply. I'm not sure I understand exactly how your scrolling works. Do you wait until 10 scroll events have been received before you start to move the bitmap? I had imagined that, in order to make the screen display feel fully responsive I would need to redraw bitmap onto the Canvas as soon as the first scroll event was received. And, because my "drawing stuff" is quite time-consuming, I decided to move this out of the onDraw method in an attempt to make the program as responsive as possible to the scroll.