]> git.zerfleddert.de Git - FreeShisen/blob - src/de/cwde/freeshisen/ShisenShoView.java
get rid of paint in doDraw.
[FreeShisen] / src / de / cwde / freeshisen / ShisenShoView.java
1 package de.cwde.freeshisen;
2
3 import java.util.List;
4 import java.util.Locale;
5 import java.util.Timer;
6 import java.util.TimerTask;
7
8 import android.app.Activity;
9 import android.content.Context;
10 import android.graphics.Bitmap;
11 import android.graphics.BitmapFactory;
12 import android.graphics.Canvas;
13 import android.graphics.Color;
14 import android.graphics.Matrix;
15 import android.graphics.Paint;
16 import android.graphics.Paint.Align;
17 import android.graphics.Paint.Cap;
18 import android.graphics.Paint.Join;
19 import android.graphics.Paint.Style;
20 import android.graphics.Rect;
21 import android.graphics.Typeface;
22 import android.os.Handler;
23 import android.os.Message;
24 import android.view.MenuItem;
25 import android.view.MotionEvent;
26 import android.view.SurfaceHolder;
27 import android.view.SurfaceView;
28
29 class ShisenShoView extends SurfaceView implements SurfaceHolder.Callback {
30
31 private static final String INVALID_TIME = "9:99:99";
32 private static final String COLOR_TEXT = "#FFFFFF";
33 private static final String COLOR_TEXT_SHADOW = "#000000";
34 private static final String COLOR_HINT = "#F0C000";
35 private static final String COLOR_SELECTED = "#FF0000";
36
37 private enum StatePlay { UNINITIALIZED, IDLE, SELECTED1, SELECTED2, GAMEOVER };
38 private enum StatePaint { BOARD, SELECTED1, SELECTED2, MATCHED, WIN, LOSE, HINT, TIME };
39
40 private int screenWidth;
41 private int screenHeight;
42 private int tilesetRows;
43 private int tilesetCols;
44 private int tileHeight;
45 private int tileWidth;
46 private Bitmap bg;
47 private Bitmap tile[];
48 private Point selection1 = new Point(0,0);
49 private Point selection2 = new Point(0,0);
50 private List<Point> path=null;
51 private List<Line> pairs=null;
52 private long startTime;
53 private long playTime;
54 private long baseTime;
55 private Timer timer;
56 private static Handler timerHandler;
57
58 private boolean timerRegistered=false;
59 private ShisenSho app;
60 private StatePlay cstate;
61 private StatePaint pstate;
62 private Canvas canvas = null;
63 private SurfaceHolder surfaceHolder = null;
64 private String time = INVALID_TIME;
65
66 public ShisenShoView(ShisenSho shishenSho) {
67 super((Context) shishenSho);
68 this.app = shishenSho;
69 cstate = StatePlay.UNINITIALIZED;
70 surfaceHolder = getHolder();
71 surfaceHolder.addCallback(this);
72 }
73
74 public ShisenShoView(Context ctx) {
75 super((Context) ctx);
76 // silence lint?
77 }
78
79 private void paint(StatePaint pstate) {
80 this.pstate=pstate;
81 repaint();
82 }
83
84 private void control(StatePlay cstate) {
85 this.cstate=cstate;
86 }
87
88 private void loadTileset() {
89 BitmapFactory.Options ops = new BitmapFactory.Options();
90 ops.inScaled = false;
91 Bitmap tileset = BitmapFactory.decodeResource(getResources(), R.drawable.tileset, ops);
92 tileset.setDensity(Bitmap.DENSITY_NONE);
93
94 // The tile set has 4 rows x 9 columns
95 tilesetRows = 4;
96 tilesetCols = 9;
97 int loadedtileWidth = tileset.getWidth()/tilesetCols;
98 int loadedtileHeight = tileset.getHeight()/tilesetRows;
99 tile = new Bitmap[tilesetRows*tilesetCols];
100
101 // align to screen:
102 // "large" is 16x6, and we want to have a nice border, so we use 17x7 and
103 // choose the lowest scale so everything fits
104 float scalex = ((float) screenWidth/17) / loadedtileWidth;
105 float scaley = ((float) screenHeight/7) / loadedtileHeight;
106 if (scaley < scalex) {
107 scalex = scaley;
108 } else {
109 scaley = scalex;
110 }
111 Matrix matrix = new Matrix();
112 matrix.setScale(scalex, scaley);
113
114 int k=0;
115 for (int i=0; i<tilesetRows; i++) {
116 for (int j=0; j<tilesetCols; j++) {
117 tile[k] = Bitmap.createBitmap(tileset, j*loadedtileWidth, i*loadedtileHeight,
118 loadedtileWidth, loadedtileHeight, matrix, false);
119 tile[k].setDensity(Bitmap.DENSITY_NONE);
120 k++;
121 }
122 }
123 tileWidth = tile[0].getWidth();
124 tileHeight = tile[0].getHeight();
125 }
126
127 private void loadBackground() {
128 BitmapFactory.Options ops = new BitmapFactory.Options();
129 ops.inScaled = false;
130 bg = BitmapFactory.decodeResource(getResources(), R.drawable.kshisen_bgnd, ops);
131 bg.setDensity(Bitmap.DENSITY_NONE);
132 }
133
134 private void registerTimer() {
135 if (timer!=null) return; // Already registered
136 timerHandler = new Handler() {
137 public void handleMessage(Message msg) {
138 onUpdateTime();
139 }
140 };
141 timer=new Timer();
142 timer.scheduleAtFixedRate(new TimerTask() {
143 public void run() {
144 timerHandler.sendEmptyMessage(Activity.RESULT_OK);
145 }
146 }, 0, 1000);
147 timerRegistered=true;
148 }
149
150 private void unregisterTimer() {
151 if (timer==null) return; // Already unregistered
152 timer.cancel();
153 timer = null;
154 timerHandler = null;
155 timerRegistered=false;
156 }
157
158 public void pauseTime() {
159 updateTime();
160 baseTime = playTime;
161 startTime = System.currentTimeMillis();
162
163 }
164
165 public void resumeTime() {
166 startTime = System.currentTimeMillis();
167 updateTime();
168 }
169
170 private void updateTime() {
171 if (cstate!=StatePlay.GAMEOVER) {
172 playTime = (System.currentTimeMillis()-startTime)/1000+baseTime;
173 }
174 }
175
176 private void initializeGame() {
177 loadBackground();
178 screenWidth=getWidth();
179 screenHeight=getHeight();
180 loadTileset();
181 //undo.sensitive=false;
182 pstate=StatePaint.BOARD;
183 app.newPlay();
184 control(StatePlay.IDLE);
185 startTime=System.currentTimeMillis();
186 playTime=0;
187 baseTime=0;
188 if (app.timeCounter && !timerRegistered) {
189 registerTimer();
190 }
191 pairs=app.board.getPairs(1);
192 }
193
194 public boolean onOptionsItemSelected(MenuItem item) {
195 // Handle item selection
196 switch (item.getItemId()) {
197 case R.id.hint:
198 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
199 return true;
200 case R.id.undo:
201 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
202 return true;
203 case R.id.clean:
204 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
205 return true;
206 case R.id.options:
207 return true;
208 case R.id.about:
209 return true;
210 default:
211 return false;
212 }
213 }
214
215 public void reset() {
216 control(StatePlay.UNINITIALIZED);
217 paint(StatePaint.BOARD);
218 }
219
220 private void onHintActivate() {
221 if (cstate!=StatePlay.GAMEOVER) {
222 pairs=app.board.getPairs(1);
223 paint(StatePaint.HINT);
224 app.sleep(10);
225 paint(StatePaint.BOARD);
226 control(StatePlay.IDLE);
227 }
228 }
229
230 private void onUndoActivate() {
231 if (app.board.getCanUndo()) {
232 if (cstate==StatePlay.GAMEOVER && app.timeCounter && !timerRegistered) {
233 // Reprogram the time update that had been
234 // deactivated with the game over status
235 registerTimer();
236 }
237 app.board.undo();
238 paint(StatePaint.BOARD);
239 //undo.sensitive=app.board.getCanUndo();
240 control(StatePlay.IDLE);
241 }
242 }
243
244 public void onTimeCounterActivate() {
245 if (app.timeCounter && cstate!=StatePlay.GAMEOVER && !timerRegistered) {
246 // Reprogram the time update that had been
247 // deactivated with the time_counter=false
248 registerTimer();
249 }
250 }
251
252 private void onUpdateTime() {
253 paint(pstate);
254 if (!(app.timeCounter && cstate!=StatePlay.GAMEOVER)) {
255 unregisterTimer();
256 }
257 }
258
259 @SuppressWarnings("deprecation")
260 public static void drawMessage(Canvas canvas, int x, int y,
261 boolean centered, String message, float textSize) {
262 Paint paint = new Paint();
263 paint.setLinearText(true);
264 paint.setAntiAlias(true);
265 paint.setTextAlign(centered ? Align.CENTER : Align.LEFT);
266 paint.setTypeface(Typeface.SANS_SERIF);
267 paint.setFakeBoldText(true);
268 paint.setTextSize(textSize);
269 paint.setColor(Color.parseColor(COLOR_TEXT_SHADOW));
270 canvas.drawText(message, x + 1, y + 1, paint);
271 paint.setColor(Color.parseColor(COLOR_TEXT));
272 canvas.drawText(message, x, y, paint);
273 }
274
275 public void repaint() {
276 if (surfaceHolder == null) return;
277 try {
278 if (canvas == null) canvas = surfaceHolder.lockCanvas(null);
279 if (canvas == null) return;
280 if (cstate==StatePlay.UNINITIALIZED) initializeGame();
281 synchronized (surfaceHolder) {
282 doDraw(canvas);
283 }
284 } finally {
285 if (canvas != null) {
286 surfaceHolder.unlockCanvasAndPost(canvas);
287 canvas = null;
288 }
289 }
290 }
291
292 protected void doDraw(Canvas canvas) {
293 try {
294 if (canvas == null) return;
295
296 // Board upper left corner on screen
297 int x0=0;
298 int y0=0;
299
300 if (app!=null && app.board!=null) {
301 x0=(screenWidth-app.board.boardSize[1]*tileWidth)/2;
302 y0=(screenHeight-app.board.boardSize[0]*tileHeight)/2;
303 }
304
305 int selectcolor = Color.parseColor(COLOR_SELECTED);
306 int hintcolor = Color.parseColor(COLOR_HINT);
307
308 // Background & board painting
309 switch (pstate) {
310 case BOARD:
311 case SELECTED1:
312 case SELECTED2:
313 case MATCHED:
314 case WIN:
315 case LOSE:
316 case HINT:
317 case TIME:
318 // Background painting
319 int bgWidth = bg.getWidth();
320 int bgHeight = bg.getHeight();
321 for (int i=0; i<screenHeight/bgHeight+1; i++) {
322 for (int j=0; j<screenWidth/bgWidth+1; j++) {
323 canvas.drawBitmap(bg, j*bgWidth, i*bgHeight, null);
324 }
325 }
326
327 // Board painting
328 // Max visible size: 7x17
329 if (app!=null && app.board!=null) {
330 for (int i=0;i<app.board.boardSize[0];i++) {
331 for (int j=0;j<app.board.boardSize[1];j++) {
332 // Tiles are 56px height, 40px width each
333 char piece=app.board.board[i][j];
334 if (piece!=0) {
335 canvas.drawBitmap(tile[piece], x0+j*tileWidth, y0+i*tileHeight, null);
336 }
337 }
338 }
339 }
340 break;
341 }
342
343 // rectangle for selection 1
344 switch (pstate) {
345 case SELECTED1:
346 case SELECTED2:
347 case MATCHED:
348 highlightTile(canvas, x0, y0, selection1, selectcolor);
349 break;
350 }
351
352 // rectangle for selection 2
353 switch (pstate) {
354 case SELECTED2:
355 case MATCHED:
356 highlightTile(canvas, x0, y0, selection2, selectcolor);
357 break;
358 }
359
360 // Matching path
361 switch (pstate) {
362 case MATCHED:
363 if (path!=null) {
364 Point p0=null;
365 for (Point p1 : path) {
366 if (p0!=null) {
367 drawLine(canvas, x0, y0, p0, p1, selectcolor);
368 }
369 p0=p1;
370 }
371 }
372 break;
373 }
374
375 // hint rectangles
376 switch (pstate) {
377 case HINT:
378 if (pairs != null && pairs.size() > 0) {
379 Line pair = pairs.get(0);
380 Point a = pair.a;
381 Point b = pair.b;
382 path = app.board.getPath(a, b);
383
384 highlightTile(canvas, x0, y0, a, hintcolor);
385
386 if (path != null) {
387 Point p0 = null;
388 for (Point p1 : path) {
389 if (p0 != null) {
390 drawLine(canvas, x0, y0, p0, p1, hintcolor);
391 }
392 p0 = p1;
393 }
394 path = null;
395 }
396
397 highlightTile(canvas, x0, y0, b, hintcolor);
398 }
399 break;
400 }
401
402 // Win & loose notifications
403 switch (pstate) {
404 case WIN:
405 drawMessage(canvas, screenWidth / 2, screenHeight / 2, true,
406 "You Win!", 100);
407 break;
408 case LOSE:
409 drawMessage(canvas, screenWidth / 2, screenHeight / 2, true,
410 "Game Over", 100);
411 break;
412 }
413
414 switch (pstate) {
415 case BOARD:
416 case SELECTED1:
417 case SELECTED2:
418 case MATCHED:
419 case WIN:
420 case LOSE:
421 case HINT:
422 case TIME:
423 updateTime();
424 int hours = (int) (playTime / (60 * 60));
425 int minutes = (int) ((playTime / 60) % 60);
426 int seconds = (int) (playTime % 60);
427 if (hours < 10) {
428 time = String.format(Locale.US, "%01d:%02d:%02d",
429 hours, minutes, seconds);
430 } else {
431 time = INVALID_TIME;
432 }
433
434 int timePosX=screenWidth-120;
435 int timePosY=screenHeight-10;
436
437 if (app.timeCounter) {
438 drawMessage(canvas, timePosX, timePosY, false, time, 30);
439 }
440 break;
441 }
442
443 } catch (Exception e) {
444 e.printStackTrace();
445 }
446
447 }
448
449 private void drawLine(Canvas canvas, int x0, int y0, Point p0, Point p1, int color) {
450 Paint paint = new Paint();
451 paint.setFlags(Paint.ANTI_ALIAS_FLAG);
452 paint.setColor(color);
453 paint.setStyle(Style.STROKE);
454 paint.setStrokeCap(Cap.ROUND);
455 paint.setStrokeJoin(Join.ROUND);
456 paint.setStrokeWidth(3);
457 canvas.drawLine(
458 x0 + p0.j * tileWidth - 2 + (tileWidth / 2),
459 y0 + p0.i * tileHeight - 2 + (tileHeight / 2),
460 x0 + p1.j * tileWidth - 2 + (tileWidth / 2),
461 y0 + p1.i * tileHeight - 2 + (tileHeight / 2), paint);
462 }
463
464 private void highlightTile(Canvas canvas, int x0, int y0, Point p, int color) {
465 Paint paint = new Paint();
466 paint.setFlags(Paint.ANTI_ALIAS_FLAG);
467 paint.setColor(color);
468 paint.setStyle(Style.STROKE);
469 paint.setStrokeCap(Cap.ROUND);
470 paint.setStrokeJoin(Join.ROUND);
471 paint.setStrokeWidth(3);
472 Rect r = new Rect(
473 x0 + p.j * tileWidth - 2,
474 y0 + p.i * tileHeight - 2,
475 x0 + p.j * tileWidth + tileWidth + 2,
476 y0 + p.i * tileHeight + tileHeight + 2);
477 canvas.drawRect(r, paint);
478 }
479
480 @Override
481 public boolean onTouchEvent(MotionEvent event) {
482 if (event.getAction()==MotionEvent.ACTION_DOWN) {
483 onClick(Math.round(event.getX()),Math.round(event.getY()));
484 }
485 return super.onTouchEvent(event);
486 }
487
488 private void onClick(int x, int y) {
489 try {
490 int i=(y-(screenHeight-app.board.boardSize[0]*tileHeight)/2)/tileHeight;
491 int j=(x-(screenWidth-app.board.boardSize[1]*tileWidth)/2)/tileWidth;
492
493 switch (cstate) {
494 case IDLE:
495 if (i >= 0 && i < app.board.boardSize[0] && j >= 0
496 && j < app.board.boardSize[1]
497 && app.board.board[i][j] != 0) {
498 selection1.set(i, j);
499 paint(StatePaint.SELECTED1);
500 control(StatePlay.SELECTED1);
501 }
502 break;
503 case SELECTED1:
504 if (i >= 0 && i < app.board.boardSize[0] && j >= 0
505 && j < app.board.boardSize[1]
506 && app.board.board[i][j] != 0) {
507 if (selection1.equals(i, j)) {
508 paint(StatePaint.BOARD);
509 control(StatePlay.IDLE);
510 } else {
511 selection2.set(i, j);
512 paint(StatePaint.SELECTED2);
513
514 Point a = selection1.copy();
515 Point b = selection2.copy();
516 path = app.board.getPath(a, b);
517 paint(StatePaint.MATCHED);
518 app.sleep(2);
519 paint(StatePaint.BOARD);
520 if (path.size() > 0) {
521 app.board.play(a, b);
522 }
523 path = null;
524 paint(StatePaint.BOARD);
525
526 pairs = app.board.getPairs(1);
527 if (pairs.size() == 0) {
528 if (app.board.getNumPieces() == 0) {
529 paint(StatePaint.WIN);
530 } else {
531 paint(StatePaint.LOSE);
532 }
533 control(StatePlay.GAMEOVER);
534 } else {
535 control(StatePlay.IDLE);
536 }
537 //undo.sensitive=app.board.getCanUndo();
538 }
539 }
540 break;
541 case GAMEOVER:
542 reset();
543 paint(StatePaint.BOARD);
544 break;
545 }
546 } catch (Exception e) {
547 e.printStackTrace();
548 }
549 }
550
551 public void surfaceChanged(SurfaceHolder holder, int format, int width,
552 int height) {
553 surfaceHolder = holder;
554 if (cstate!=StatePlay.GAMEOVER && app.timeCounter && !timerRegistered) {
555 registerTimer();
556 }
557 repaint();
558 }
559
560 public void surfaceCreated(SurfaceHolder holder) {
561 surfaceHolder = holder;
562 repaint();
563 }
564
565 public void surfaceDestroyed(SurfaceHolder holder) {
566 surfaceHolder = null;
567 if (timerRegistered) {
568 unregisterTimer();
569 }
570 }
571 }
Impressum, Datenschutz