Nieuwe app : Wie Ben Ik ?

Ik was op zoek naar een leuk spelletje, dat je met veel mensen kunt spelen. Bijvoorbeeld om elkaar iets beter te leren kennen, samen te lachen of gewoon als tijdverdrijf. Het moest een spelletje zijn dat je kunt spelen in een groep, maar ook met z’n 2en. Misschien zelfs wel in de trein, met mensen die je niet kent. Tijdens een weekend van Mensa speelden we “WelkSpel”. Een geweldig leuk spelletje met 3 rondes. In ronde één moet je een omschrijving geven van een persoon, je team moet raden “wie je bent”. Ik begon wat te fröbelen en, tadaa : Wie ben ik op je Telefoon!

Dus vandaag heb ik weer een app gepubliceerd in de Market. Ik ben nog bezig met de verfijning en ben dan ook erg nieuwsgierig naar jullie op- en aanmerkingen. Sowieso zit er nog wat werk in de vertaling. Daarnaast ben ik bezig met een andere GamePlay. We noemen het “WelkSpel” en het lijkt een beetje op de ronde ‘raadt de bekende Nederlander’ van Ik Hou van Holland. Het ontwikkelen van deze app ging behoorlijk goed! Inmiddels beginnen de JAVA tutorials hun vruchten af te werpen en begrijp ik een beetje hoe je Object Oriented moet programmeren. Een behoorlijke stapel nieuwe inzichten sinds ik begon (lees daarover hier). Ik zal proberen de gehele WieBenIk app hier toe te lichten. Daarom zal ik in dit bericht grote stukken code tonen, die ik aanvul met opmerkingen (gemarkeerd met “//”).

Ik was begonnen met het schrijven van een applicatie die nauwkeurig registreert hoe de telefoon wordt vastgehouden (de Orientation). Dat om te proberen het woord alleen te tonen als de speler naar de achterkant van de telefoon kijkt. Dat bleek echter heel lastig. Daarnaast heeft niet alle hardware de beschikking over nauwkeurige sensoren, waardoor er vaak flitsen van het woord toch nog zichtbaar waren. Uiteindelijk heb ik dit vervangen door een methode “instructPlayerToShowScreen()”.

De mainActivity ziet er zo uit :

package nl.happyworx.wiebenik;

import java.io.IOException;
import java.util.Random;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Vibrator;
import android.util.Log;
import android.view.Gravity;
import android.view.OrientationEventListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;

public class WieBenIkActivity extends Activity {

	public TextView introTextView;
	public TextView hintTextView;
	public CountDownTimer guessingCountDownTimer;
	public boolean countDownFinished;

	public Button btn_start;
	public ViewFlipper flipper;
	public ScrollTextView toGuessText;
	public String objectToGuess;

	public View objectchallengeview;
	public int state;

	public ShakeListener myShakeListener;
	public DataBaseHelper myDBHelper;

	private ProgressBar pbar_showingToGuess;
	private ProgressBar pbar_guessTime;
	private Vibrator myVibrator;
	public MediaPlayer myMediaPlayer;

	public OrientationEventListener myOrientationEventListener;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		flipper = (ViewFlipper) findViewById(R.id.flipper);
		introTextView = (TextView) findViewById(R.id.introTextView1);
		hintTextView = (TextView) findViewById(R.id.hintTextView);
		pbar_showingToGuess = (ProgressBar) findViewById(R.id.progressBar1);
		pbar_guessTime = (ProgressBar) findViewById(R.id.pbar_guessTime);
		toGuessText = (ScrollTextView) findViewById(R.id.toGuessText);

		myVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
		myShakeListener = new ShakeListener(this);
		myDBHelper = new DataBaseHelper(this);

		// Kopieer de database met woorden naar de telefoon als deze er nog niet op staat
		// Dit stukje heb ik geleend uit één van mijn andere apps, FlitsWoorden.
		// Omdat ik nu begrijp hoe het OO model inelkaar zit, is de code wel veel schoner
		// geworden.

		try {
			myDBHelper.createDataBase();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		btn_start = (Button) findViewById(R.id.btn_Start);
		btn_start.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				getRndWord();
				instructPlayerToShowScreen();
			}
		});

	}

	// Dit is een methode om een willekeurig woord uit de database te halen
	public void getRndWord() {
		// Log.d("WieBenIkActivity", "Count : " + myDBHelper.getMaxRecord());
		// Log.d("WieBenIkActivity", "Word : " + myDBHelper.getWord(5));
		Random rnd = new Random();
		int rndrecord = rnd.nextInt(myDBHelper.getMaxRecord());
		Log.d("WieBenIk", "Random : " + rndrecord);
		objectToGuess = "--- " + myDBHelper.getWord(rndrecord);

	}

	// Dit is de code om de speler te instrueren het scherm aan de andere spelers
	// te laten zien.
	public void instructPlayerToShowScreen() {
		flipper.setDisplayedChild(0);
		btn_start.setClickable(false);
		introTextView.setText(R.string.show_screen_to_other_players_txt);
		introTextView.setGravity(Gravity.CENTER);
		introTextView.setTextSize(20);
		PlaySound(R.raw.danzegikdiekenik_bite_11s);
		startCountdownTimer(10000, pbar_showingToGuess);
	}

	// Hier een stuk code om te voorkomen dat de telefoon het scherm opnieuw gaat opbouwen
	// als je hem op z'n kop houdt
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);

	}

	// De countdowntimer had ik al eerder gebouwd, voor Applez. Ook hier : doordat ik nu
	// beter begrijp wat de code precies doet, is het allemaal veel schoner geworden.
	// Het was mijn bedoeling om hier een universele CountDowntimer te bouwen, die ik
	// in de hele app kon gebruiken (vandaar de verwijzing naar de ProgressBar).
	// Het blijkt echter veel makkelijker om in een paar regels een nieuwe timer aan te
	// maken, daar waar je hem nodig hebt.
	private void startCountdownTimer(int ms_total, final ProgressBar progress) {
		countDownFinished = false;
		pbar_showingToGuess.setProgress(ms_total);

		final int totalMsecs = ms_total;
		int callInterval = 100;

		/** CountDownTimer */
		new CountDownTimer(totalMsecs, callInterval) {

			public void onTick(long millisUntilFinished) {

				float fraction = millisUntilFinished / (float) totalMsecs;

				// progress bar is based on scale of 1 to 100;
				progress.setProgress((int) (fraction * 100));
			}

			public void onFinish() {
				showToGuess();
			}
		}.start();
	}

	// Wel een aparte methode voor het afspelen van geluidjes. De MediaPlayer heeft best een
	// lastige lifecycle. Je moet zorgen dat je de onPrepared Override, zodat het geluid helemaal
	// geladen is voordat het wordt afgespeeld.
	public void PlaySound(int sound) {
		if (myMediaPlayer != null) {
			myMediaPlayer = null;
		}
		myMediaPlayer = MediaPlayer.create(getApplicationContext(), sound);

		myMediaPlayer.setOnPreparedListener(new OnPreparedListener() {
			@Override
			public void onPrepared(MediaPlayer inPlayer) {
				myMediaPlayer.start();
			}
		});
		myMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
			@Override
			public void onCompletion(MediaPlayer player) {
				Log.d("Sound", "Sound Finito");
				player.release();
			}
		});

	}

	// Stukkie code om het woord te laten zien. Ik gebruik hier een ViewFlipper
	// Wat een uitvinding is dat! Je kunt, zonder dat je allerlei lastige intent-
	// calls moet doen, toch een ander schermpje tonen!
	private void showToGuess() {

		btn_start.setClickable(true);
		flipper.setInAnimation(inFromBottomAnimation(200));
		flipper.setOutAnimation(outToTopAnimation(200));
		flipper.setDisplayedChild(1);

		toGuessText.setText(objectToGuess);
		toGuessText.setTextColor(Color.BLACK);
		toGuessText.startScroll();
		myShakeListener.start();

		Button btn_Woops = (Button) findViewById(R.id.Woops);
		btn_Woops.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				myShakeListener.stop();
				guessingCountDownTimer.cancel();
				instructPlayerToShowScreen();

			}
		});

	}

	// En dan gaan we raden naar het woord. Ik ga dit nog een beetje 'variabeliseren'
	// zodat de speler kan instellen hoe lang de raad-tijd zou moeten zijn.
	// Hier zijn ook de buttons gedefinieerd. Uiteindelijk moeten die buttons een soort
	// scorelijstje gaan bijhouden, maar dat is wat minder belangrijk voor de GamePlay
	public void startGuess() {
		myShakeListener.stop();
		flipper.setDisplayedChild(2);
		myVibrator.vibrate(500);
		guessingCountDownTimer = new CountDownTimer(30000, 100) {
			public void onTick(long millisUntilFinished) {
				float fraction = millisUntilFinished / (float) 30000;
				pbar_guessTime.setProgress((int) (fraction * 100));
			}

			public void onFinish() {
				// Please note : alle texten verwijzen naar variabelen. Zo kan ik het geheel
				// makkelijk vertalen. De variabelen kun je terugvinden in je strings.xml
				// in de res\values map.
				hintTextView.setText(R.string.did_you_guess_correctly_txt);
			}
		}.start();
		findViewById(R.id.btn_correct).setOnClickListener(
				new OnClickListener() {

					@Override
					public void onClick(View v) {
						Toast.makeText(getApplicationContext(), "GOOD JOB!!",
								Toast.LENGTH_LONG).show();
						guessingCountDownTimer.cancel();
						onCreate(null);
					}
				});
		findViewById(R.id.btn_wrong).setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Toast.makeText(getApplicationContext(), "Try Again",
						Toast.LENGTH_LONG).show();
				guessingCountDownTimer.cancel();
				onCreate(null);

			}
		});

	}

	// En helemaal onderaan definieer ik de animaties voor de ViewFlipper
	// Dat zou ook waarschijnlijk netjes in een aparte Interface kunnen
	// maar zo helemaal onderaan in de code doen ze niemand kwaad 🙂
	private Animation inFromBottomAnimation(int duration) {

		Animation infromBottom = new TranslateAnimation(
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, +1.0f,
				Animation.RELATIVE_TO_PARENT, 0.0f);
		infromBottom.setDuration(duration);
		infromBottom.setInterpolator(new AccelerateInterpolator());
		return infromBottom;
	}

	private Animation outToTopAnimation(int duration) {
		Animation outToTop = new TranslateAnimation(
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, -1.0f);
		outToTop.setDuration(duration);
		outToTop.setInterpolator(new AccelerateInterpolator());
		return outToTop;
	}

	private Animation inFromTopAnimation() {
		Animation inFromTop = new TranslateAnimation(
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, -1.0f,
				Animation.RELATIVE_TO_PARENT, 0.0f);
		inFromTop.setDuration(500);
		inFromTop.setInterpolator(new AccelerateInterpolator());
		return inFromTop;
	}

	private Animation outToBottomAnimation() {
		Animation outToBottom = new TranslateAnimation(
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, 0.0f,
				Animation.RELATIVE_TO_PARENT, +1.0f);
		outToBottom.setDuration(500);
		outToBottom.setInterpolator(new AccelerateInterpolator());
		return outToBottom;
	}

}

Voor de lastige bewerkingen (de DataBase, de scrolltextview en de shakelistener) heb ik aparte classes gebouwd. Ik zal ze toelichten in een andere post.

WieBenIk is nog LANG niet klaar.. zo moet er bijvoorbeeld nog in : Categorieën (zodat het ook met kinderen goed te spelen is), instellen van speeltijd, de nieuwe game-mode (WelkSpel), en als ik echt helemaal los ga : suggesties die je kunt doen voor aanvulling van de database (maar dan moet ik een stuk serverside programmeren.. en dat heb ik nog nooit gedaan 😉 ).

Oh ja.. wat wel een absolute must gaat worden : een check of je de meest recente database hebt. Zo niet, direct downloaden. Dat gaat dus één of andere spiffy FTP interface worden, met een check op een bestandsnaam ofzo..

Wat helemaal nice zou zijn : Na ieder woord kun je een aantal sterren geven. Op die manier kan ik nieuwe woorden introduceren en laat ik de rapportage bepalen welke woorden blijven..

 PS : I’m thinking about switching to English for this blog. Please leave a comment if you would like to read in English! — Make sure the comment doesn’t look like spam, though!  😉

Nota Bene : Voor de database heb ik aan m’n Mensa vriendjes gevraagd woordjes op te sturen. Dat werd massaal gedaan! Dus bij deze; dank aan Matthias Wagenaar, Luc Lacroix, Nathalie Bouts, Fanny Cattenstart, Yessica, Bas Warmenhoven, Vivien Kandou, Ineke Buitendijk, Laurence Steenbergen, Caroline Verdonk, Krister Horn, Hobbyfun.nl (check de site! :), Ton Bil, Wilma Voets

You can leave a response, or trackback from your own site.

2 Responses to “Nieuwe app : Wie Ben Ik ?”

  1. Michiel schreef:

    Probeer de laatste link (hobbyfun) met http:// ervoor, dan werkt hij wel goed. Verder vind ik het wel leuk dat je de blog in het Nederlands doet.

  2. admin schreef:

    Thanks! Link aangepast.. 🙂

Leave a Reply