Creating a Viewport in Android

I was writing a simple PacMan like game for Android, and needed to scroll the game area shown as the user moves inside it. In J2ME, you will use LayerManager.setViewWindow to create this kind of Viewport like behaviour. In Android, this can be achieved by using methods in Canvas and View classes.

Here is a simple program demonstrating how to do it –

public class AViewport extends Activity
{

 public void onCreate(Bundle savedInstanceState)
 {
 super.onCreate(savedInstanceState);
 setContentView( new PortView( this ) );

 }

 private class PortView extends View
 {
 // the length and width of a single square
 private int tileSide = 50;
 // the number of squares along x-axis and y-axis
 private int numTiles = 15;

 int fieldWidth = numTiles * tileSide;
 int fieldHeight = numTiles * tileSide;

 private int halfViewWidth;
 private int halfViewHeight;
 private int maxTranslateX;
 private int maxTranslateY;

 int viewWidth = 200;
 int viewHeight = 200;

 private int circleX;
 private int circleY;
 private int diameter = 50;

 private ShapeDrawable one;
 private ShapeDrawable two;

 private ShapeDrawable circle;
 Rect rect = new Rect();
 private Paint p;

 public PortView( Context context )
 {
 super( context );
 one = new ShapeDrawable( new RectShape() );
 two = new ShapeDrawable( new RectShape() );
 circle = new ShapeDrawable( new OvalShape() );
 one.getPaint().setColor( 0x88FF8844 );
 two.getPaint().setColor( 0x8844FF88 );
 circle.getPaint().setColor( 0x99000000 );
 p = new Paint();
 setFocusable( true );
 }

 @Override
 protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec )
 {
 int width = Math.min( fieldWidth, MeasureSpec.getSize( widthMeasureSpec ) );
 int height = Math.min( fieldHeight, MeasureSpec.getSize( heightMeasureSpec ) );

 viewWidth = width;
 viewHeight = height;
 halfViewWidth = width/2;
 halfViewHeight = height/2;
 maxTranslateX = fieldWidth - width;
 maxTranslateY = fieldHeight - height;

 setMeasuredDimension( width, height );
 }

/*
 _______________________________________
 | ______________                        |
 ||              |                       |
 ||              |vH                     |
 ||              |                       |
 ||______________|                       |
 |       vW                              |fH
 |       .(x1, y1)         ______________|
 |                        |              |
 |                        |              |
 |<---------------------->|              |
 |     maxTranslateX      |______________|
 |_______________________________________|
 fW

 We start translating once x,y goes past x1 (viewWidth/2),y1 (viewHeight/2).
 Movement along x-axis is x - x1, to the maximum of maxTranslateX (fieldWidth - viewWidth).
 The movement along y-axis is calculated similarly.
*/
 @Override
 protected void onDraw( Canvas canvas )
 {
 super.onDraw( canvas );

 canvas.save();

 if ( circleX > halfViewWidth )
 {
 int translateX = Math.min( circleX - halfViewWidth, maxTranslateX );
 canvas.translate( -translateX, 0 );
 }

 if ( circleY > halfViewHeight )
 {
 int translateY = Math.min( circleY - halfViewHeight, maxTranslateY );
 canvas.translate( 0, -translateY );
 }

 drawBoard( canvas );
 drawCircle( canvas );
 canvas.restore();
 }

 private void drawBoard( Canvas canvas )
 {
 int num = 1;
 boolean useTwo = false;
 for ( int row = 0; row < numTiles; row++ )
 {
 int y = row * tileSide;
 for ( int col = 0; col < numTiles; col++ )
 {
 int x = col * tileSide;
 Drawable d = useTwo ? two : one;
 d.setBounds( x, y, x + tileSide, y + tileSide );
 d.draw( canvas );
 canvas.drawText( "" + num, x + 10 , y + 20, p );
 ++num;
 useTwo = !useTwo;
 }
 }
 }

 private void setRect()
 {
 rect.set( circleX, circleY, circleX + diameter, circleY + diameter );
 }

 private void drawCircle( Canvas canvas )
 {
 setRect();
 circle.setBounds( rect );
 circle.draw( canvas );
 }

 @Override
 public boolean onKeyDown( int keyCode, KeyEvent keyEvent )
 {
 boolean handled = true;
 switch ( keyCode )
 {
 case KeyEvent.KEYCODE_DPAD_DOWN:
 if ( circleY <= fieldHeight - diameter - 5 ) circleY += 5;
 break;
 case KeyEvent.KEYCODE_DPAD_UP:
 if( circleY >= 5) circleY -= 5;
 break;
 case KeyEvent.KEYCODE_DPAD_LEFT:
 if( circleX >= 5 ) circleX -= 5;
 break;
 case KeyEvent.KEYCODE_DPAD_RIGHT:
 if( circleX <= fieldWidth - diameter -5 ) circleX += 5;
 break;
 default: handled = false;
 }
 if ( handled )
 {
 invalidate();
 }
 return handled;
 }
 }
}

See the Android API docs of View.onMeasure() and Canvas.translate() methods to understand the code used to create the Viewport.

Setting up WordPress on Ubuntu and Nginx

Finally, after thinking about it many times and never giving it a try, I moved my blog over from wordpress.com to a hosted wordpress installation.

Thanks to kind folks at mensk, the setup was a breeze.
See Perfect Setup: Ubuntu Hardy+Nginx+MySQL5+PHP5+Wordress

If you are following the instructions on that post though, be careful. The first instruction for

sudo ln -s /usr/local/nginx/sites-available/mydomain.com /usr/local/nginx/sites-enabled/mydomain.com

should be

sudo ln -s /usr/local/nginx/sites-available/default /usr/local/nginx/sites-enabled/default

Otherwise, you will get a connection refused error when you try to test just after completing the Nginx setup. Other than that, I was able to get everything up and running with minimal fuss.

This is the first time I am playing with Nginx, that was pretty cool. As soon as I have some time, I should see how well it works with Tomcat.

And, thanks to good people at jestro for writing the Vigilance WordPress theme. It is a minimalistic theme that lets you control many aspects of the blog. If you are going to customize any of the styles in the theme, make sure to read Easy Upgrading With Child Themes.