Android Adventures - Beginning Bitmap Graphics
Written by Mike James   
Monday, 27 July 2015
Article Index
Android Adventures - Beginning Bitmap Graphics
A First Graphic
Simple Animation
Timer and threads
Listing and Summary

A Logical Way With Transforms

One approach to keeping track of transformations is to draw everything centered on the origin, and then translate, scale and rotate it to its final position. For example, to draw the rectangle 

c.drawRect(20F, 300F, 180F, 400F, paint);

You would first draw a unit square centered on the origin:

c.drawRect(-0.5F, -0.5F, 0.5F, 0.5F, paint);

Then you would then scale it to its desired size:

c.scale(160,100);

and rotate it:

c.rotate(45F);

Finally you would move it to its correct location:

c.translate(100F, 350F);

If you try out these steps, you will discover that you don't get what you expect. The reason is that we have been transforming the object - draw a square, scale the square, rotate it and move it to the desired location. However, the Canvas transformations don't transform graphical objects but the coordinate system. You can immediately see that in this case you draw the square last when you have performed all of the transformations. Indeed this is the rule - do everything you would have done to the geometric shape in the reverse order when you are working with the coordinates. 

So the correct transformation sequence is:

c.save();
c.translate(100F, 350F);
c.rotate(45F);
c.scale(160,100);
c.drawRect(-0.5F, -0.5F, 0.5F, 0.5F, paint);
c.restore();

You can always work out what the transformation sequence you need is by considering the graphical object, working out the transforms needed to change it to what you want and do them in the reverse order. 

Some programmers take to this idea and think its great and the only way to do logical systematic graphics. Some adopt it a little bit and others draw things where they are needed in the size and orientation needed. 

Setting Your Own Coordinates

When you first attach a Canvas to a Bitmap the coordinate system is in terms of the number of pixels in the Bitmap. Often, however, you want to work with a different coordinate system. For example, you might want to work with the origin in the middle and the x and y coordinate ranging from -1 to +1.

You can set any coordinate system you care to work with using suitable transformations.

If your coordinate system runs from xmin to xmax and from ymin to ymax you can apply it to the canvas using:

c.scale(width/(xmax-xmin),height/(ymax-ymin));
c.translate(-xmin,-ymin);

where width and height are the size in pixels of the bitmap. 

Using this formulation the y coordinate increases down the screen as did the original pixel coordinates. 

If you want the y coordinate to increase up the screen then use the transformation:

c.scale(width/(xmax-xmin),-height/(ymax-ymin));
c.translate(-xmin,-ymax);

 and notice the change to ymax in the second line.

So, for example, if you wanted to draw a graph using coordinates between 0,0 in the bottom left corner and 10,10 in the top right, i.e. the y increasing up the screen, you would use:

c.save();
float xmax=10;
float xmin=0;
float ymax=10;
float ymin=0;
float width=500;
float height=500;
c.scale(width / (xmax - xmin), -height / (ymax - ymin));
c.translate(-xmin, -ymax);
paint.setStrokeWidth(.4f);
c.drawLine(0F, 0F, 10F, 0F, paint);
c.drawLine(0F, 0F, 0F, 10F, paint);
c.drawLine(0F, 0F, 10F, 10F, paint);

c.restore();
 

 

This draws axes and a 45 degree line:

 

chart

 

Notice that the axes only show because they are drawn thick enough to make it onto the bitmap. Also notice that when you change the coordinate system all other measurements change as well and hence the stroke width has to be set to 0.4 - it is no longer in terms of pixels. 

Simple Animation

To bring this chapter to a close we will animate a ball bouncing around a Canvas, or a Bitmap depending how you look at it. This might seem like a strange topic to end on, especially since we are not going to do the job in the way that most Android programmers would. Indeed Android has a range of different animation facilities - View animation, value animation and so on. However, none of them demonstrate the fundamental way that dynamic graphics work. Before you move on to learn more sophisticated ways of creating animation it is a good idea to find out how things work at the lowest level. It not only teaches you something about animation but about the problems of creating dynamic graphics of any kind in the Android UI. 

One warning - do not assume this is all there is to know about Android animation.

To animate something in the simplest and most direct way all you have to do is draw the shape, change the shape, erase the old graphic, and draw it again. 

In most systems this is usually achieved at the lowest possible level by using a timer to call an update function which erases the shape, does the update to the shape and then draws it at its new location. You can take this approach in Android, but for various reasons it isn't the way things are usually done. It has to be admitted that there are some slight difficulties, but overcoming them isn't hard and it is very instructive.

To see how it all works let's just bounce a "ball" around the screen. This is more or less the "hello world" of simple 2D sprite based graphics.

So start a new Android application and place an ImageView on the design surface - this is the only UI element we need. 

In the OnCreate method, after the UI has been expanded all we need to do is set things up a bit more:

Bitmap b = Bitmap.createBitmap(width,
                               height,
                    Bitmap.Config.ARGB_8888);
c = new Canvas(b);
c.drawColor(Color.WHITE);

First we create a bitmap, associate it with a Canvas and then set the whole bitmap to white. Notice that other parts of the program are going to need to access width, height and c so they are defined as global variables:

private int width=800, height=800;
private Canvas c;

We are also going to need global variables to record the ball's position, radius and its velocity. For simplicity we might as well just use the default pixel coordinates of the Bitmap:

private float x=463,y=743,vx=1,vy=1,r=30;

Other functions are also going to need to access the ImageView control so it might as well be a global variable so that we don't have to waste time finding each time it is needed:

private ImageView imageview;

and for the same reason a global Paint object saves time:

private Paint paint;

Now we have all of these variables defined we can move on with the OnCreate function and set up the Paint object:

paint = new Paint();
paint.setAntiAlias(false);
paint.setStyle(Paint.Style.FILL);

You might be wondering why AntiAlias is set to false - the reason is that it's dithering algorithm makes it hard to remove a graphic by redrawing it in the background color. Try changing false to true in the final program to see what the problem is. 

We also need to get the ImageView object and set the bitmap we are drawing on to display:

imageview=(ImageView) findViewById(R.id.imageView);imageview.setImageBitmap(b);

 

Androidgears



Last Updated ( Wednesday, 12 October 2016 )