Home > Guide e tutorial per Android > Sviluppare un Gioco per Android – Lezione 10: Utilizzare Font Bitmap

Sviluppare un Gioco per Android – Lezione 10: Utilizzare Font Bitmap

programmazione-android10Android, come saprete, è un sistema operativo molto utilizzato su vari tipi di terminali di dimensioni diverse. Dopo aver visto come creare esplosioni, è giunta l’ora di affrontare un’altra questione: i font. Come possiamo utilizzare i caratteri per scrivere testo nei nostri giochi Android? Un’idea consiste nell’utilizzare font bitmap. Sappiamo già come utilizzare le immagini e, se ne avessimo una per ogni lettera e numero potremo scrivere facilmente del testo, tuttavia non è eccessivo caricare un’immagine per ogni carattere? La risposta è si. Per fortuna ci sono i metodi Android a venirci in aiuto.

Tutto ciò di cui abbiamo bisogno è una sola immagine contente tutti i caratteri di cui abbiamo bisogno e, per semplicità, utilizzeremo un font a spaziatura fissa, ossia con i caratteri della stessa larghezza. Una semplice mappa di caratteri potrebbe essere la seguente:

glyphs_black

A questo punto, la semplicità nell’utilizzare font bitmap è evidente. I caratteri sono disposti a griglia, ognuno di questi è alto 12 pixel e largo 8, naturalmente vi è un po’ di spazio tra una riga e l’altra. Una volta sapute queste informazioni, grazie alle lezioni precedenti, dovreste sapere tutti come tagliare i singoli caratteri.

glyph_big

Come potete vedere dall’immagine in alto tratta da Photoshop, i caratteri sono sempre organizzati in celle con una dimensione ben precisa, dunque il lavoro da fare non sarà poi tanto difficile, basterà un semplice ciclo per ritagliare ogni singolo carattere. Possiamo utilizzare, naturalmente, ogni font bitmap che desideriamo. A questo punto, vediamo come lavorare sul codice. 

Creiamo il progetto

L’idea sulla quale ci baseremo è quella di utilizzare un metodo, drawString(), passando come parametri la tela sulla quale disegneremo, la posizione (quindi le coordinate x e y) e la stringa da visualizzare. A questo punto, possiamo creare un semplice progetto Android che utilizza un oggetto canvas 2D e implementiamo, come sempre, SurfaceView. Nelle risorse avremo il seguente file che fungerà da alfabeto per le nostre stringhe (sempre in /res/drawable-mdpi).

glyphs_green

La classe che andremo a creare la chiameremo DrawingPanel.java e potete vederla qui di seguito:

public class DrawingPanel extends SurfaceView implements SurfaceHolder.Callback {

	private static final String TAG = DrawingPanel.class.getSimpleName();

	private Canvas canvas;		// la tela per disegnare
	private Glyphs glyphs;		// i caratteri

	public DrawingPanel(Context context) {
		super(context);
		getHolder().addCallback(this);

		// Inizialliziamo le risorse
		loadResources();

		setFocusable(true);
	}

 	private void loadResources() {
		this.glyphs = new Glyphs(BitmapFactory.decodeResource(getResources(), R.drawable.glyphs_green));
		Log.d(TAG, "Green glyphs loaded");
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// disegnare testo al tocco
		try {
			canvas = getHolder().lockCanvas();
			synchronized (getHolder()) {
				if (event.getAction() == MotionEvent.ACTION_DOWN 
						|| event.getAction() == MotionEvent.ACTION_MOVE) {
					canvas.drawColor(Color.BLACK);
					// disegna i caratteri
					glyphs.drawString(canvas, "Drawing string at "
							+ (int) event.getX() + " " + (int) event.getY(),
							(int) event.getX(), (int) event.getY());
				}
				if (event.getAction() == MotionEvent.ACTION_UP) {
					canvas.drawColor(Color.BLACK);
					glyphs.drawString(canvas, "Drawn string at "
							+ (int) event.getX() + " " + (int) event.getY(),
							(int) event.getX(), (int) event.getY());
				}
			}
		} finally {
			if (canvas != null) {
				getHolder().unlockCanvasAndPost(canvas);
			}
		}

		return true;
	}

	@Override
	protected void onDraw(Canvas canvas) {
	}

}

Come potete vedere, non c’è bisogno di implementare tutti i metodi per il nostro scopo. Utilizziamo l’oggetto canvas per disegnare la nostra stringa ad ogni tocco, quindi prestate maggiore attenzione a onTouchEvent(). Ogni volta che tocchiamo il display cerchiamo di entrare in possesso della tela per disegnare la stringa e, con il tocco prolungato (ACTION_MOVE), noterete cambiare leggermente il testo.

Infine, liberiamo la tela. Assicuratevi di inserire return true per segnalare che l’evento è stato gestito senza problemi. A questo punto, diamo un’occhiata alla classe più interessante: Glyphs.java. Come avete visto, ne viene creata un’istanza nel metodo loadResources() e, come parametro, le passiamo l’immagine inserita prima nelle risorse. Essa detiene l’associazione tra caratteri e immagini, vediamo come. 

La classe Glyphs.java

public class Glyphs {

	private static final String TAG = Glyphs.class.getSimpleName();
	private Bitmap bitmap;	// bitmap contenente la mappa di caratteri

	// Mappa per associare ogni carattere alla bitmap
	private Map<Character, Bitmap> glyphs = new HashMap<Character, Bitmap>(62);

	private int width;	// larghezza di ogni carattere in pixel
	private int height;	// altezza di ogni carattere in pixel

	// I caratteri
	private char[] charactersL = new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
			'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
			'u', 'v', 'w', 'x', 'y', 'z' };
	private char[] charactersU = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G',
			'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
			'U', 'V', 'W', 'X', 'Y', 'Z' };
	private char[] numbers = new char[] { '1', '2', '3', '4', '5', '6', '7',
			'8', '9', '0' };

	public Glyphs(Bitmap bitmap) {
		super();
		this.bitmap = bitmap;
		this.width = 8;
		this.height = 12;
		// ritagliamo i caratteri
		// iniziamo dalla prima riga - Caratteri minuscoli
		for (int i = 0; i < 26; i++) {
			glyphs.put(charactersL[i], Bitmap.createBitmap(bitmap,
					0 + (i * width), 0, width, height));
		}
		Log.d(TAG, "Lowercases initialised");

		// Continuiamo con la seconda riga - Caratteri maiuscoli
		// Notare che la seconda riga inizia più in basso di 15 pixel
		for (int i = 0; i < 26; i++) {
			glyphs.put(charactersU[i], Bitmap.createBitmap(bitmap,
					0 + (i * width), 15, width, height));
		}
		// riga 3 per i numeri
		Log.d(TAG, "Uppercases initialised");
		for (int i = 0; i < 10; i++) {
			glyphs.put(numbers[i], Bitmap.createBitmap(bitmap,
					0 + (i * width), 30, width, height));
		}
		Log.d(TAG, "Numbers initialised");

		// TODO - riga 4 per la punteggiatura
	}

	public Bitmap getBitmap() {
		return bitmap;
	}

	public void drawString(Canvas canvas, String text, int x, int y) {
		if (canvas == null) {
			Log.d(TAG, "Canvas is null");
		}
		for (int i = 0; i < text.length(); i++) {
			Character ch = text.charAt(i);
			if (glyphs.get(ch) != null) {
				canvas.drawBitmap(glyphs.get(ch), x + (i * width), y, null);
			}
		}
	}
}

Al rigo 7 istanziamo l’oggetto di tipo Map che ci servirà ad associare la bitmap ad ogni carattere. Per questa funzione, tuttavia, abbiamo bisogno delle bitmap, ma anche di un array che contenga tutti i caratteri che abbiamo nella nostra immagine. Per comodità, ne abbiamo creati tre: charactersL per le lettere minuscole, charactersU per le maiuscole e numbers per i numeri. Per quanto riguarda la punteggiatura, invece, potete provare ad implementarla voi stessi.

Si noti che l’ordine dei caratteri è lo stesso di quello che troviamo nell’immagine caricata. Successivamente, utilizziamo il metodo put() per associare ad ogni elemento dell’array l’immagine bitmap ritagliata con le giuste dimensioni. Ritaglio e associazione sono le attività più importanti del costruttore.

Infine, abbiamo il metodo con il quale abbiamo iniziato questa decima lezione: drawString(). Esso scorre ogni carattere del testo disegnando la relativa immagine associata ad ogni carattere.

Infine, ecco la consueta classe entry point MainActivity.java, che a questo punto dovreste conoscere molto bene:

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
	        super.onCreate(savedInstanceState);
	        requestWindowFeature(Window.FEATURE_NO_TITLE);
	        setContentView(new DrawingPanel(this));

	}

}

Una volta avviata l’applicazione, dovreste avere un risultato molto simile al seguente:

Screenshot from 2014-03-03 15:58:49