Statically Typed

because Hindley-Milner rocks

Android: Avoiding Custom Views with Resources


Creating a custom view is hard.  It’s hard not because there’s something technically difficult about it but rather because there’s so little literature out there to guide you in how you should start.  Go ahead and Google it.  What you’ll find is that almost every reference on the first page relates to extending an existing Android view.

When I first started thinking about creating a custom view I should have stopped.  There is a reason why there’s such a scarcity of articles/posts about them and that reason is for the majority of applications they’re unnecessary.  The questions I should have asked myself before I researched the topic were as follows:

  • Did the custom functionality I need belong to the view, one of the listeners, or within the activity itself?
  • Was there an existing view which covered more than 50% of what I wanted?  What about a combination of several views?
  • Could I avoid a lot of code mangling by a judicious use of resource xml files?

Once I asked myself these questions the answer was obvious, everything I needed could be accomplished by extending something that already existed.  My goals were not so alien or even special that their engineers hadn’t thought or provided a much easier way for me to proceed.

To demonstrate, let’s say that I’m building a chess program.  I need my view to be a chess board with 8 x 8 black and white squares.  I should be able to place a chess piece onto each individual square and interact with that square.  I might add more advanced logic (like being able to play the game) but for now, that’ll suffice.

Think about those simple requirements.  First of all they’re simple.  Not in the usual sense (it’s not going to take 6 weeks, dude) but in the real sense.  Does it make sense to create a super chess board view object with intricate knowledge of its layout, routines for figuring out where on the board the user is, and what he’s doing to each square?  Or, does it make more sense to turn each square into its own view using standard Android resources to customize the behavior?

One will produce a byzantine structure with potentially easy to screw up code and the other will rely on what other engineers have already built for us.  Some combination of views, augmenting an existing view, selection of ready-made resource objects and we’re ready to go.

A Chess Square

The first thing I want to accomplish is to draw a square in the first place.  So let me set up two resource files, one for black and one for white squares.  I’ll create an XML file in the /drawable/ folder called white_square.xml:

<?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="12dp"
		android:height="12dp" />
	<solid android:color="#FFFFFF" />
</shape>

and another one called black_square.xml with #000000 as the color.

The most basic drawable displaying view is known as an ImageView.  All it does is hold a drawable with the added ability to attach listeners and/or extend it.  I want to be able to draw a square and the interaction logic seems reasonable enough to place in some other object:

public class ChessSquareView extends ImageView{
	private IChessPiece mPiece = new NullPiece();
	public ChessSquareView(Context context){
		super(context);
		setClickable(true);
	}
        public ChessSquareView(Context context, AttributeSet attributes) {
		super(context, attributes);
		setClickable(true);
	}
	public void setPiece(IChessPiece _piece){
		mPiece = _piece;
	}
	public IChessPiece getPiece(){
		return mPiece;
	}
        @Override
	protected void onDraw(Canvas _canvas){
		super.onDraw(_canvas);
		mPiece.draw(_canvas, getPaddingLeft(), getPaddingTop());
	}
}

When I call this view, all I need to do is attribute it with the drawable I want it to display and the chess piece I want it to hold.  As a default, I’ll instantiate it with a chess piece that doesn’t draw a thing.

A Chess Board

Next I’ll set up the board.  Since the board will never change, only what’s on it, there’s really no reason I should programmatically instantiate it.  But populating 64 squares by hand is nauseating.  It’s wrist work and I hate wrist work.  A script wouldn’t complain, so let’s use a bit of Python (2.7) code to write the layout:

letters = map(chr, range(97,105)) #historically squares go from 'a' to 'h'
color = ["white_square", "black_square"]
def write_square(x,y):
	print "\t<com.owein.ChessSquareView"
	if(x != 0):
		print "\t\tandroid:layout_below=\"@id/" + letters[x-1] + str(y+1) + "\""
	if(y != 0):
		print "\t\tandroid:layout_toRightOf=\"@id/" + letters[x] + str(y) + "\""
	print "\t\tandroid:background=\"@drawable/" + color[(x+y)%2] + "\""
	print "\t\tandroid:layout_width=\"wrap_content\""
	print "\t\tandroid:layout_height=\"wrap_content\""
	print "\t\tandroid:id=\"@+id/" + letters[x] + str(y+1) + "\" />"

There, now I’ve got the basics for a square given two integers x and y.  Filling out the rest of my board.xml file, that is destined for the /layout/ folder, and calling that short Python script in a nested loop ([0…8], [0…8]):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  	xmlns:android="http://schemas.android.com/apk/res/android"
  	android:layout_width="fill_parent"
  	android:layout_height="fill_parent">
	<com.owein.ChessSquareView
		android:background="@drawable/white_square"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:id="@+id/a1" />
.
.
.
</RelativeLayout>

(Truncated for size.)  For code, it doesn’t get much simpler:

public class Board extends Activity{
	@Override
	public void onCreate(Bundle _savedInstanceState){
		super.onCreate(_savedInstanceState);
		setContentView(R.layout.board);
	}
}

Remember, I haven’t added any logic yet…

An App with a View

Up to this point, all seems well and good.  So what’s it look like?

Hrmph!  Looks like 12dp is not enough space to suit me.  I’ll have to increase it if I want anyone to take my chess game seriously.  We can’t all be born with toothpicks for hands and binoculars for eyes.

In the next post I’ll take this simple layout to the next step, adding in a portion of the model and control.  (MVC for the win!)

Advertisements

2 comments on “Android: Avoiding Custom Views with Resources

  1. Ruyam
    August 25, 2011

    Hi,
    can I get the complete working code for this….

  2. Pingback: android moving View containing RelativeLayout - feed99

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 January 26, 2011 by in Android, Java, Python and tagged .
%d bloggers like this: