/
PuzzleActivity.java
293 lines (244 loc) · 11.8 KB
/
PuzzleActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
package dragosholban.com.androidpuzzlegame;
import android.content.Intent;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import static java.lang.Math.abs;
public class PuzzleActivity extends AppCompatActivity {
ArrayList<PuzzlePiece> pieces;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_puzzle);
final RelativeLayout layout = findViewById(R.id.layout);
final ImageView imageView = findViewById(R.id.imageView);
Intent intent = getIntent();
final String assetName = intent.getStringExtra("assetName");
// run image related code after the view was laid out
// to have all dimensions calculated
imageView.post(new Runnable() {
@Override
public void run() {
if (assetName != null) {
setPicFromAsset(assetName, imageView);
}
pieces = splitImage();
TouchListener touchListener = new TouchListener(PuzzleActivity.this);
// shuffle pieces order
Collections.shuffle(pieces);
for (PuzzlePiece piece : pieces) {
piece.setOnTouchListener(touchListener);
layout.addView(piece);
// randomize position, on the bottom of the screen
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) piece.getLayoutParams();
lParams.leftMargin = new Random().nextInt(layout.getWidth() - piece.pieceWidth);
lParams.topMargin = layout.getHeight() - piece.pieceHeight;
piece.setLayoutParams(lParams);
}
}
});
}
private void setPicFromAsset(String assetName, ImageView imageView) {
// Get the dimensions of the View
int targetW = imageView.getWidth();
int targetH = imageView.getHeight();
AssetManager am = getAssets();
try {
InputStream is = am.open("img/" + assetName);
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, new Rect(-1, -1, -1, -1), bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
is.reset();
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeStream(is, new Rect(-1, -1, -1, -1), bmOptions);
imageView.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
}
private ArrayList<PuzzlePiece> splitImage() {
int piecesNumber = 12;
int rows = 4;
int cols = 3;
ImageView imageView = findViewById(R.id.imageView);
ArrayList<PuzzlePiece> pieces = new ArrayList<>(piecesNumber);
// Get the scaled bitmap of the source image
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
Bitmap bitmap = drawable.getBitmap();
int[] dimensions = getBitmapPositionInsideImageView(imageView);
int scaledBitmapLeft = dimensions[0];
int scaledBitmapTop = dimensions[1];
int scaledBitmapWidth = dimensions[2];
int scaledBitmapHeight = dimensions[3];
int croppedImageWidth = scaledBitmapWidth - 2 * abs(scaledBitmapLeft);
int croppedImageHeight = scaledBitmapHeight - 2 * abs(scaledBitmapTop);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, scaledBitmapWidth, scaledBitmapHeight, true);
Bitmap croppedBitmap = Bitmap.createBitmap(scaledBitmap, abs(scaledBitmapLeft), abs(scaledBitmapTop), croppedImageWidth, croppedImageHeight);
// Calculate the with and height of the pieces
int pieceWidth = croppedImageWidth/cols;
int pieceHeight = croppedImageHeight/rows;
// Create each bitmap piece and add it to the resulting array
int yCoord = 0;
for (int row = 0; row < rows; row++) {
int xCoord = 0;
for (int col = 0; col < cols; col++) {
// calculate offset for each piece
int offsetX = 0;
int offsetY = 0;
if (col > 0) {
offsetX = pieceWidth / 3;
}
if (row > 0) {
offsetY = pieceHeight / 3;
}
// apply the offset to each piece
Bitmap pieceBitmap = Bitmap.createBitmap(croppedBitmap, xCoord - offsetX, yCoord - offsetY, pieceWidth + offsetX, pieceHeight + offsetY);
PuzzlePiece piece = new PuzzlePiece(getApplicationContext());
piece.setImageBitmap(pieceBitmap);
piece.xCoord = xCoord - offsetX + imageView.getLeft();
piece.yCoord = yCoord - offsetY + imageView.getTop();
piece.pieceWidth = pieceWidth + offsetX;
piece.pieceHeight = pieceHeight + offsetY;
// this bitmap will hold our final puzzle piece image
Bitmap puzzlePiece = Bitmap.createBitmap(pieceWidth + offsetX, pieceHeight + offsetY, Bitmap.Config.ARGB_8888);
// draw path
int bumpSize = pieceHeight / 4;
Canvas canvas = new Canvas(puzzlePiece);
Path path = new Path();
path.moveTo(offsetX, offsetY);
if (row == 0) {
// top side piece
path.lineTo(pieceBitmap.getWidth(), offsetY);
} else {
// top bump
path.lineTo(offsetX + (pieceBitmap.getWidth() - offsetX) / 3, offsetY);
path.cubicTo(offsetX + (pieceBitmap.getWidth() - offsetX) / 6, offsetY - bumpSize, offsetX + (pieceBitmap.getWidth() - offsetX) / 6 * 5, offsetY - bumpSize, offsetX + (pieceBitmap.getWidth() - offsetX) / 3 * 2, offsetY);
path.lineTo(pieceBitmap.getWidth(), offsetY);
}
if (col == cols - 1) {
// right side piece
path.lineTo(pieceBitmap.getWidth(), pieceBitmap.getHeight());
} else {
// right bump
path.lineTo(pieceBitmap.getWidth(), offsetY + (pieceBitmap.getHeight() - offsetY) / 3);
path.cubicTo(pieceBitmap.getWidth() - bumpSize,offsetY + (pieceBitmap.getHeight() - offsetY) / 6, pieceBitmap.getWidth() - bumpSize, offsetY + (pieceBitmap.getHeight() - offsetY) / 6 * 5, pieceBitmap.getWidth(), offsetY + (pieceBitmap.getHeight() - offsetY) / 3 * 2);
path.lineTo(pieceBitmap.getWidth(), pieceBitmap.getHeight());
}
if (row == rows - 1) {
// bottom side piece
path.lineTo(offsetX, pieceBitmap.getHeight());
} else {
// bottom bump
path.lineTo(offsetX + (pieceBitmap.getWidth() - offsetX) / 3 * 2, pieceBitmap.getHeight());
path.cubicTo(offsetX + (pieceBitmap.getWidth() - offsetX) / 6 * 5,pieceBitmap.getHeight() - bumpSize, offsetX + (pieceBitmap.getWidth() - offsetX) / 6, pieceBitmap.getHeight() - bumpSize, offsetX + (pieceBitmap.getWidth() - offsetX) / 3, pieceBitmap.getHeight());
path.lineTo(offsetX, pieceBitmap.getHeight());
}
if (col == 0) {
// left side piece
path.close();
} else {
// left bump
path.lineTo(offsetX, offsetY + (pieceBitmap.getHeight() - offsetY) / 3 * 2);
path.cubicTo(offsetX - bumpSize, offsetY + (pieceBitmap.getHeight() - offsetY) / 6 * 5, offsetX - bumpSize, offsetY + (pieceBitmap.getHeight() - offsetY) / 6, offsetX, offsetY + (pieceBitmap.getHeight() - offsetY) / 3);
path.close();
}
// mask the piece
Paint paint = new Paint();
paint.setColor(0XFF000000);
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(pieceBitmap, 0, 0, paint);
// draw a white border
Paint border = new Paint();
border.setColor(0X80FFFFFF);
border.setStyle(Paint.Style.STROKE);
border.setStrokeWidth(8.0f);
canvas.drawPath(path, border);
// draw a black border
border = new Paint();
border.setColor(0X80000000);
border.setStyle(Paint.Style.STROKE);
border.setStrokeWidth(3.0f);
canvas.drawPath(path, border);
// set the resulting bitmap to the piece
piece.setImageBitmap(puzzlePiece);
pieces.add(piece);
xCoord += pieceWidth;
}
yCoord += pieceHeight;
}
return pieces;
}
private int[] getBitmapPositionInsideImageView(ImageView imageView) {
int[] ret = new int[4];
if (imageView == null || imageView.getDrawable() == null)
return ret;
// Get image dimensions
// Get image matrix values and place them in an array
float[] f = new float[9];
imageView.getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = imageView.getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
ret[2] = actW;
ret[3] = actH;
// Get image position
// We assume that the image is centered into ImageView
int imgViewW = imageView.getWidth();
int imgViewH = imageView.getHeight();
int top = (int) (imgViewH - actH)/2;
int left = (int) (imgViewW - actW)/2;
ret[0] = left;
ret[1] = top;
return ret;
}
public void checkGameOver() {
if (isGameOver()) {
finish();
}
}
private boolean isGameOver() {
for (PuzzlePiece piece : pieces) {
if (piece.canMove) {
return false;
}
}
return true;
}
}