Android Games

Android App tutorial:Peg board game

I released in Github the source code of an app that can be used to play peg board. This app was published on the market some times ago and this is a new version.

The app screenshots are shown below:
 
 
 
 
 
 
 
 
 
board_1[4]

board_2[4]

board_3[4]

There are some interesting aspects we can consider in this app and they are described below. It can be used as example how to integrate different elements like SlidingPaneLayoutfragments, event handling and so on.

App structure

The main android UI pattern used by the app is the Sliding Pane layout because we need to have the screen divided in two area: the main area is where we can play with our pegs and the other one that usually is closed is the menu.

So the overall layout is:

<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sp"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:background="@drawable/tilebkg"
    >

    <fragment
        android:id="@+id/pinMenu"
        android:name="com.survivingwithandroid.pegboard.fragment.MenuFragment"
        android:layout_width="200dp"
        android:layout_height="match_parent"
        android:layout_gravity="left" 
        />

    <fragment
        android:id="@+id/pinTable"
        android:name="com.survivingwithandroid.pegboard.fragment.DreamPinTableFragment"
        android:layout_width="600dp"
        android:layout_height="match_parent"
        android:layout_gravity="right"
        android:layout_weight="1" />

</android.support.v4.widget.SlidingPaneLayout>

The result is shown below:

board_2_fragment[4]

As you can notice the SlidingPaneLayout handles two fragments DreamPinTableFragment and MenuFragment. DreamPinTableFragment takes care of creating the table and handling the user touch so that the user can place pins on it, while the MenuFragment handles the some options like choosing the pin colors, save the work and so on.

Custom Layout with ViewGroup

The peg board is implemented as custom layout. The class that implements it is called PinTableView that extends ViewGroup. This custom layout takes care of dividing the UI screen in small cells that corresponds to the holes where the pegs are placed. The app screen is treated as a table divided in rows and columns.

The first method called by DreamPinTableFragment is disposePin. This method calculates the number or rows and columns and initializes the table:

public int[] disposePins(int width, int height, int dotSize) {

    this.dotSize = dotSize;

    numRow = height / dotSize + 1;
    numCol =  width / dotSize + 1;

    int[] dotColors = Pin.createDotArray(dotSize, true);

    for (int r=0; r < numRow ; r++) {
        for (int c=0; c < numCol; c++) {
            PinImageView pinImg = new PinImageView(getContext(), r, c);
            this.addView(pinImg);               
        }
    }

    return new int[]{numRow, numCol};
}

Notice that in this method we add PinImageView (another custom view) to the layout (line…) giving to them the right row and column number.

Then in onLayout method we traverse the list of childs (we added previously) and start placing them in the right position. In this case we need to calculate the x and y coordinates in real pixel, this is simple once we know the row and column number and the cell pixel size:

@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
    int childCount = getChildCount();

    for (int i=0; i < childCount; i++) {
        PinImageView pinImg = (PinImageView) getChildAt(i);

        //int left = pinImg.getCol() * dotSize + dotSize * (pinImg.getType() == PinImageView.COLOR_COMMANDS || pinImg.getType() == PinImageView.DELETE ? 0 : 1);
        int left = pinImg.getCol() * dotSize;
        //int top = pinImg.getRow()  * dotSize + dotSize * (pinImg.getType() == PinImageView.COLOR_COMMANDS || pinImg.getType() == PinImageView.DELETE ? 0 : 1);
        int top = pinImg.getRow()  * dotSize;
        int right = left + dotSize ;
        int bottom = top + dotSize ;

        pinImg.layout(left, top, right, bottom);            
    }

}

In this way, we created an empty board that looks like the picture shown below:

board_1[8]

Custom imageView: PinImageView

As we saw before, we place in our layout a custom ImageView childs. PinImageView is a simple class that extends ImageView and represents a single Peg (or pin as we prefer to call it). This class has an internal state that holds the type of the peg it represents and implements some kind of animation to make the UI more attractive.

public class PinImageView extends ImageView  {
private int row;
private int col;    
private int xSize;
private int ySize;    
private int stato = -1;    
private Context ctx;
private int currentPinId;

public PinImageView(Context context, int row, int col) {
    super(context);
    this.ctx = context;
    this.row = row;
    this.col = col;
    //this.parent = parent;        

    // Load image
    Drawable d  = getResources().getDrawable(TableConfig.pinBackground);
    setImageDrawable(d);        
    xSize = this.getWidth();
    ySize = this.getHeight();
    this.currentPinId = TableConfig.pinBackground;

}
...
class AnimView implements Runnable {

        Animation anim;
        Drawable d;

        public AnimView(Animation anim, Drawable d) {
            this.anim = anim;
            this.d = d;
        }
        @Override
        public void run() {
            anim.setAnimationListener(new Animation.AnimationListener() {

                @Override
                public void onAnimationStart(Animation animation) {
                    TableConfig.playSound(PinImageView.this.ctx);
                }

                @Override
                public void onAnimationRepeat(Animation animation) {}

                @Override
                public void onAnimationEnd(Animation animation) {
                    setImageDrawable(d);
                    PinImageView.this.setVisibility(View.VISIBLE);
                }
            });

            PinImageView.this.startAnimation(anim);    
        }

    }
}

Handle user touch: OnTouchEvent and OnTouchListener

The next step is handling the user touch. We define that DreamPinTableFragment listens to the events triggered when user touches the screen. We want to place a peg in the position where user touched the screen:

@Override
public boolean onTouch(View v, MotionEvent event) {

    int x = (int) event.getX() ;
    int y = (int) event.getY() ;

    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        pinsTable.changePinColor(x, y);            
        return true;
    }
    else if (event.getAction() == MotionEvent.ACTION_MOVE) {
        pinsTable.changePinColor(x, y);    
        return true;
    }

    return false;

}

and then we delegate to the PinTableView to handle the event passing the coordinates:

public void changePinColor(int x, int y) {
    int row = getRow(y);
    int col = getColumn(x);

    PinImageView pinImg = (PinImageView) getChildAt(col + ( row  * numCol ));
    if (pinImg != null) {
        pinImg.setPinColor(currentColor);    

    }
}

Knowing the x and y coordinates we can calculate the corresponding row and column and set the peg with the right color.

App Menu

As we explained before the app has a menu where user can select the pincolor, save the work or change the table background. The class that handles the menu is called MenuFragment. This is very simple class, it just notifies to the main activity the events triggered when user selects a menu item. In turn, the main activity handle this event informing the other fragment when these event regards pegs or some actions that have to be taken on the table.

The MenuFragment defines an interface (a listener) that we have to implement when we want to have information on the events occurred in the menu:

public static interface MenuEventListener {
    public void onPinSelected(int pinId);
    public void onClearSelected();
    public void onSaveSelected();
    public void onBackgroundSelected();
}

So the main activity:

public class DreamPinsActivity extends Activity implements
MenuFragment.MenuEventListener {
....
    @Override
    public void onPinSelected(int pinId) {

        pinTableFrag.setCurrentPin(pinId);
        closeMenu();
    }

    @Override
    public void onClearSelected() {
        ...
    }

    @Override
    public void onSaveSelected() {
        closeMenu();
        ...
    }

    public void onBackgroundSelected() {
        ...
    }
}

Save layout as image

The last aspect we want to cover is saving the layout as image. We can implement it in this way:

in the PinsTableView:

public Bitmap createBitmap() {
    Bitmap b = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.RGB_565);        
    Canvas c = new Canvas(b);

    this.draw(c);

    return b;
}

where we create an empty bitmap having the same size of the screen and then draw on the corresponding Canvas. Once we have our bitmap that contains the screenshot of the app screen we save it in a file:

OutputStream outStream = context.getContentResolver()
        .openOutputStream(uri);

b.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.flush();
outStream.close();

 

Reference: Android App tutorial:Peg board game from our JCG partner Francesco Azzola at the Surviving w/ Android blog.

Francesco Azzola

He's a senior software engineer with more than 15 yrs old experience in JEE architecture. He's SCEA certified (Sun Certified Enterprise Architect), SCWCD, SCJP. He is an android enthusiast and he has worked for long time in the mobile development field.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button