Statically Typed

because Hindley-Milner rocks

Android: Avoiding Custom Views with Resources (cont’)


So in the previous post I was attempting to create a chessboard with some functionality all the while avoiding defining my own “made from scratch” view.  So far it seems that I’m off to a good start but is there more that can be done to the graphics before I go further with the game logic?  Yup, sure is.

It would be nice if we could highlight an individual square when a player selects it if it has a piece on it.  Better yet, it would be nice if we could also show a different highlight color for each square that piece could move to.  Those seem like reasonable requirements to add to the game (Isn’t everything reasonable before we actually have to code it?)

Adding Highlights

First thing is first.  Let’s create a drawable with a box around it that’s the same size and color as our white and black squares (now 36dp so it look presentable.)  To do this I’ll first create two other shape resources: a square with a different color, let’s say blue, like I did before and call it blue_square.xml; and a smaller dimension square:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:shape="rectangle">
	<corners android:radius="1dp" />
	<size
		android:width="30dp"
		android:height="30dp" />
	<solid android:color="#FFFFFF" />
</shape>

which I’ll dub white_inset.xml.  Next, I’m going to combine them together to make a composite drawable using a layer-list (let’s dub this one white_blue_border.xml):

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
 		android:drawable="@drawable/blue_square" />
    <item
 		android:drawable="@drawable/white_inset"
		android:top="3dp"
 		android:bottom="3dp"
		android:left="3dp"
		android:right="3dp" />
</layer-list>

When a layer-list is created, each subsequent item of the list is placed atop the previous items.  If they are not the same size Android will scale/adjust the images.  Here, the elements top, bottom, left and right prevent the white_inset image (of size 30dp) from obscuring the blue_square image (of size 36dp.)

Adding Selection

Android developers anticipated the need to have an image that changes with state and so gave us the state-list and the level-list.  The state-list is good for boolean indicators such as “pressed” or “not pressed.”  It’s elements are geared specifically for integration with Buttons, CheckBox types, and things of that nature.  The level-list, on the other hand, is a little more general.  We’ll use that to our advantage as we have more than 2 states for our chessboard square.

We have a white square, a white square with a blue border but we’re missing a third resource for our third state.  I’ll go ahead and make a white_red_border.xml following the same template outlined above.  You should be able to figure out what that would look like in XML for yourself.

These resource files can yet again be composed together to create one super resource which allows selectable image state.  This will be the master resource file which will be substituted into the layout XML file.  We’ll call it simply white.xml:

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item
        android:drawable="@drawable/white_square"
        android:maxLevel="0" />
     <item
        android:drawable="@drawable/white_red_border"
        android:maxLevel="1" />
    <item
        android:drawable="@drawable/white_blue_border"
        android:maxLevel="2" />
</level-list>

By itself, it doesn’t accomplish much.  When drawn it will display the first item in the list (not the one with the value of 0!)  In our case, that’s an ordinary white square.

Window Dressing

In order to make use of all the things we’ve just built our program will have to call ImageView#setImageLevel(int) in an intelligent way.  “Intelligent way” is my way of saying “make a stub, leave that part for another post.”  So I’ll do just that.

Let’s make an OnTouchListener which cycles through our resource states for testing and demonstration purposes.

public class CycleStateListener extends View.OnTouchListener {
     @Override
     public boolean onTouch(View arg0, MotionEvent arg1) {
		if(arg1.getAction() == MotionEvent.ACTION_DOWN){
			Drawable back = arg0.getBackground();
			switch(back.getLevel()){
			case 0:
			     Log.d("CycleListener", "Level 0");
			     back.setLevel(1);
			     break;
			case 1:
			     Log.d("CycleListener", "Level 1");
			     back.setLevel(2);
			     break;
			case 2:
			     Log.d("OnTouch", "Level 2");
			     back.setLevel(0);
			     break;
			}
			return true;
		}
	return false;
	}
}

We’ll take that an attach it to every ChessSquareView in our code to wind up with something that looks like:

Conclusion

Yet again we’ve found a way to add functionality and represent stateful actions without having to implement our own view from the ground up.  There’s very little code and subsequently very little logic we can @!#% up.  I said last time that I’ll put in more of the logic and move away from the view aspect but I’ve punted again to the next post.

Stay tuned, actually interacting with what we’ve got so far coming up.

Advertisements

6 comments on “Android: Avoiding Custom Views with Resources (cont’)

  1. Mitch
    March 25, 2011

    I’ve been looking for something like this for a while. I was wondering about your chess board because I’m trying to do something similar. I want a board that’s 3×6 of perfect squares that fills the width of it’s parent and retains the squareness of each square. I can’t seem to figure this out and it seems like it should be simple. You are close, but your screen snapshot seems to not fill the width of the parent either.

    Perhaps you could explain how to make your chess board fill the parent’s width and retain the squareness of the board?

    Excellent writing btw.

    • owreese
      March 25, 2011

      Mitch,

      The simple answer is that you have the tiles fill_parent. Then you use the RelativeLayout functionality to place each square exactly where you want it to appear in relation to its neighbors. However, that begets one small problem… uniform scaling. It won’t happen the way you think it will. That’s why I recommend adding in a buffer, sizing the squares to a certain size, and then setting the gravity to center horizonal.

      On the other hand, if you don’t mind that your length might differ from your width, there ya go. Hope I was able to get back to you in time. Just got home myself.

  2. Carl
    July 29, 2011

    Instead of the RelativeLayout used here, you could create a class that extends GridView and then invoke setStretchMode(STRETCH_COLUMN_WIDTH) in its constructor (along with setVerticalSpacing(), setHorizontalSpacing(), setGravity(), etc.). Then, just write code that populates an 8×8 array of ChessSquareView objects, and use setAdapter() to create a ListAdapter() object and override its getView() method so that it returns the appropriate ChessSquareView object from the two-dimensional array for the one-dimensional position provided as an argument (col = position % numberOfRows; row = position / numberOfCols;).

    You may, at that point, have to invoke getWindowVisibleDisplayFrame(rootViewRect) to get exact (not dp) pixels available for the width, and then just set the width and height for the layout params of your GridView-derived class to that exact value. Because although you could use MATCH_PARENT for the width, you still need actual pixels to set the height to the same value as the width so that the result is square.

    • owreese
      July 30, 2011

      What a great comment. It’s always nice to read about a different way to doing things. The Android API isn’t large but it sure is, for lack of a better description, flexible. I’ll have to try out your suggestion.

  3. Ruyam
    September 1, 2011

    Hi,
    Thanks for this tutorial…
    Can you tell me how to place a coin on the square grid and drag it to other squares……

    • owreese
      September 3, 2011

      You’re going to have to work with the touch events. They’re a little tricky to work with at first as it’s not completely intuitive that a single event might actually represent several movement points. Animations which result should probably be looked at on a few models of phones. If you’re dealing with only phones in the last year or two it shouldn’t be as big a deal as it was a few years ago. Take a look at one of the other posts under the android section.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Information

This entry was posted on February 1, 2011 by in Android, Java and tagged .
%d bloggers like this: