Android TranslateAnimation und OnClickListener

Hobelhai

Mitglied
Hallo zusammen,
um mich ein wenig in Android einzuarbeiten habe ich mir ein kleines Nummernpuzzle vorgenommen, ein Spiel, welches früher öfters (also in echter Hardware) auf der Kirmes als Tombolapreis vergeben wurde:
Kleine quadratische Täfelchen mit 5 x 5 Feldern und 24, mit den Zahlen 1 - 24 beschrifteten, Plättchen. Ziel des Spiels ist es, die Plättchen bzw. die darauf abgedruckten Zahlen durch Verschieben in die korrekte Reihenfolge zu bringen (von links nach rechts und von oben nach unten), es ist ja auf dem Täfelchen immer ein Feld unbesetzt.

Im meinem Programm habe ich das so realisiert, das ich mir 24 TextViews erzeuge, in einer Arraylist speichere und, sobald ich mir die Maße des Displays abfragen kann (in der OnDraw-Methode), diesen TextViews die passende Größe verpasse und in ein FrameLayout packe. Jede TextView bekommt das FrameLayout als OnClickListener zugewiesen. Klickt der Spieler jetzt auf eines der Plättchen, so entscheidet das Programm, ob es zu verschieben ist oder nicht. Falls es verschiebbar sein sollte (das freie Feld muss an das angeklickte Plättchen angrenzen), so wird eine TranslateAnimation gestartet, die das Plättchen an den freien Platz bewegt. So weit so gut, das funktioniert alles. Klicke ich allerdings erneut auf das Plättchen - ich kann es ja wieder auf seinen alten Platz bewegen, der ja jetzt frei geworden ist - passiert erst einmal garnichts. Der Grund: Das Plättchen reagiert nur auf Klicks, die man auf seiner alten Position ausführt, sprich: Klicke ich auf das nun freie Feld, spricht das Plättchen an und ruft die OnClick-Methode auf. Leider schweigt sich die Android-Dokumentation (die mMn sowieso etwas dürftig geraten ist) dazu aus, und auch über Google bin ich nur auf eine Lösung gestoßen (in meinem Code schon realisiert, siehe die OnAnimationEnd-Methode), die nicht wirklich funktioniert: Das Plättchen wird um ein weiteres Feld verschoben, rutscht also im Zweifelsfall ganz aus dem Spielfeld heraus. Ich weiß langsam nicht mehr weiter, bitte gebt mir einen Tip!

Java:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

public class GameBoard extends FrameLayout implements OnClickListener, AnimationListener
{
	private List<TextView> tiles;
	private boolean initialised = false;
	private int currentVacantRow;
	private int currentVacantColumn;
	private int width;
	public enum Direction {NORTH, EAST, SOUTH, WEST};
	private int row;
	private int column;
	private TextView currentTile;
	private int tileSize;
	private int animDuration = 1000;
	
	public GameBoard(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		createTiles(context);
	}

	@Override
	public void onClick(View v)
	{
		currentTile = (TextView) v;
		row = (Integer) currentTile.getTag(R.id.row);
		column = (Integer) currentTile.getTag(R.id.column);
		MoveEvent me = this.checkMovables();
		if (me != null)
		{
			currentTile.setBackgroundResource(R.drawable.tilegreen);
			if (me.getDirection() == Direction.NORTH)
			{
				moveNorth();
			}
			else if (me.getDirection() == Direction.EAST)
			{
				moveEast();
			}
			else if (me.getDirection() == Direction.SOUTH)
			{
				moveSouth();
			}
			else
			{
				moveWest();
			}
		}
		else
		{
			currentTile.setBackgroundResource(R.drawable.tilered);
		}
	}

	@Override
	public void onAnimationEnd(Animation arg0)
	{
		currentTile.setBackgroundResource(R.drawable.tileblue);
		FrameLayout.LayoutParams layPar = new FrameLayout.LayoutParams(tileSize, tileSize);
		layPar.leftMargin = (Integer) currentTile.getTag(R.id.xpos);
		layPar.topMargin = (Integer) currentTile.getTag(R.id.ypos);
		layPar.gravity = Gravity.TOP + Gravity.LEFT;
		currentTile.setLayoutParams(layPar);
		this.requestLayout();
	}

	public void buildNewGame()
	{
		this.removeAllViews();
		this.createTiles(getContext());
		this.initialise();
	}

	@Override
	protected void onDraw(Canvas canvas)
	{
		super.onDraw(canvas);
		if (!initialised)
		{
			width = this.getWidth();
			tileSize = (width - (2 * (width / 16))) / 5;
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, width);
			this.setLayoutParams(params);
			initialise();
		}
	}

	private void createTiles(Context context)
	{
		tiles = new ArrayList<TextView>();
		for (int i = 1; i < 25; i++)
		{
			TextView tv = new TextView(context);
			tv.setBackgroundResource(R.drawable.tileblue);
			tv.setText(String.valueOf(i));
			tv.setGravity(Gravity.CENTER);
			tv.setTextColor(Color.BLACK);
			tiles.add(tv);
			tiles.get(i - 1).setOnClickListener(this);
		}
		Collections.shuffle(tiles);
	}

	private void initialise()
	{
		initialised = true;		
		int x = width / 16;
		int y = width / 16;		
		int counter = 0;
		for (int row = 0; row < 5; row++)
		{
			for (int column = 0; column < 5; column++)
			{
				if (counter == 24)
				{
					break;
				}
				else
				{
					TextView nt = tiles.get(counter);
					nt.setTag(R.id.row, row);
					nt.setTag(R.id.column, column);
					FrameLayout.LayoutParams layPar = new FrameLayout.LayoutParams(tileSize, tileSize);
					layPar.leftMargin = x;
					layPar.topMargin = y;
					nt.setTag(R.id.xpos, x);
					nt.setTag(R.id.ypos, y);
					layPar.gravity = Gravity.TOP + Gravity.LEFT;
					this.addView(nt, layPar);
					counter++;
					if (column == 4)
					{
						x = width / 16;
					}
					else
					{
						x += tileSize;
					}
				}
			}
			y += tileSize;
		}
		currentVacantRow = 4;
		currentVacantColumn = 4;
	}

	private MoveEvent checkMovables()
	{
		MoveEvent me = null;
		if (row == currentVacantRow)
		{
			if (column == currentVacantColumn - 1)
			{
				me = new MoveEvent(Direction.EAST);
			}
			else if (column == currentVacantColumn + 1)
			{
				me = new MoveEvent(Direction.WEST);
			}
			
		}
		else if (column == currentVacantColumn)
		{
			if (row == currentVacantRow - 1)
			{
				me = new MoveEvent(Direction.SOUTH);
			}
			else if (row == currentVacantRow + 1)
			{
				me = new MoveEvent(Direction.NORTH);
			}
		}
		return me;
	}

	private void moveNorth()
	{
		TranslateAnimation anim = new TranslateAnimation(0, 0, 0, -tileSize);
		anim.setAnimationListener(this);
		anim.setDuration(animDuration);
		anim.setFillAfter(true);
		currentTile.startAnimation(anim);
		currentTile.setTag(R.id.row, currentVacantRow);
		currentVacantRow = row;
		int y = ((Integer) currentTile.getTag(R.id.ypos)) - tileSize;
		currentTile.setTag(R.id.ypos, y);
	}

	private void moveEast()
	{
		TranslateAnimation anim = new TranslateAnimation(0, tileSize, 0, 0);
		anim.setAnimationListener(this);
		anim.setDuration(animDuration);
		anim.setFillAfter(true);
		currentTile.startAnimation(anim);
		currentTile.setTag(R.id.column, currentVacantColumn);
		currentVacantColumn = column;
		int x = ((Integer) currentTile.getTag(R.id.xpos)) + tileSize;
		currentTile.setTag(R.id.xpos, x);
	}

	private void moveSouth()
	{
		TranslateAnimation anim = new TranslateAnimation(0, 0, 0, tileSize);
		anim.setAnimationListener(this);
		anim.setDuration(animDuration);
		anim.setFillAfter(true);
		currentTile.startAnimation(anim);
		currentTile.setTag(R.id.row, currentVacantRow);
		currentVacantRow = row;
		int y = ((Integer) currentTile.getTag(R.id.ypos)) + tileSize;
		currentTile.setTag(R.id.ypos, y);		
	}

	private void moveWest()
	{
		TranslateAnimation anim = new TranslateAnimation(0, -tileSize, 0, 0);
		anim.setAnimationListener(this);
		anim.setDuration(animDuration);
		anim.setFillAfter(true);
		currentTile.startAnimation(anim);
		currentTile.setTag(R.id.column, currentVacantColumn);
		currentVacantColumn = column;
		int x = ((Integer) currentTile.getTag(R.id.xpos)) - tileSize;
		currentTile.setTag(R.id.xpos, x);
	}

	@Override
	public void onAnimationRepeat(Animation animation)
	{}

	@Override
	public void onAnimationStart(Animation animation)
	{}
}
 

schlingel

Gesperrter Benutzer
Die Herangehensweise mit dem FrameLayout und den TextViews halte ich für äußerst unvorteilhaft da du versuchst beidem gerade Verhalten beizubringen für das sie nicht gedacht sind. Plakativ gesagt, kämpfst du gerade gegen das Framework anstatt es zu verwenden.

Ich würde an deiner Stelle von vorne anfangen und die Plättchen mit den Zahlen selbst in einem Canvas oder ähnlichem Zeichnen.
 

Hobelhai

Mitglied
Schon mal Danke für deinen Ratschlag. So etwas ähnliches hatte ich befürchtet. Dann werde ich die App wohl umbauen - und damit dann auch die Plättchen "von Hand" animieren müssen. Schade eigentlich, die Animationsfeatures von Android finde ich ja ganz reizvoll, erspart einem einiges an Arbeit.
 

Neue Themen


Oben