如何知道我触摸了网格 (10x10) 的哪个单元格?
How to know which cell of a grid (10x10) I've touched?
我正在使用 Canvas 和我使用 canvas.drawRect 绘制的 10x10 网格创建一个扫雷器。问题是,我不知道如何获取任何单元格的坐标(例如:在我的网格上的位置 (2,3) 中,物理坐标是什么?)。也许我没有正确地做网格? (我是初学者..)但是我必须使用Canvas,没有图形。我在这里看到了一些答案,但没有真正帮助我..
代码如下:
package com.example.meinsweeper_ja;
import java.util.Random;
import android.annotation.SuppressLint;
import android.content.*;
import android.graphics.*;
import android.util.*;
import android.view.*;
public class CustomView extends View {
private Paint black, grey, white, blue, green, yellow, red;
private Rect square, edge;
private boolean grid[][]; // true = uncovered false = covered
private boolean mines[][]; // true = mine false = no mine
private int arround[][]; // Nb of mine around
private float touchx[];
private float touchy[];
private int nb_mines, nb_ard;
public CustomView(Context c) {
super(c);
init();
}
public CustomView(Context c, AttributeSet as) {
super(c, as);
init();
}
public CustomView(Context c, AttributeSet as, int default_style) {
super(c, as, default_style);
init();
}
private void init() {
black = new Paint(Paint.ANTI_ALIAS_FLAG);
grey = new Paint(Paint.ANTI_ALIAS_FLAG);
white = new Paint(Paint.ANTI_ALIAS_FLAG);
yellow = new Paint(Paint.ANTI_ALIAS_FLAG);
red = new Paint(Paint.ANTI_ALIAS_FLAG);
green = new Paint(Paint.ANTI_ALIAS_FLAG);
blue = new Paint(Paint.ANTI_ALIAS_FLAG);
black.setColor(0xFF000000);
grey.setColor(0xFF808080);
white.setColor(0xFFFFFFFF);
yellow.setColor(0xFFFFFF00);
red.setColor(0xFFFF0000);
green.setColor(0xFF00FF00);
blue.setColor(0xFF0000FF);
touchx = new float[10];
touchy = new float[10];
grid = new boolean[10][10];
mines = new boolean[10][10];
arround = new int[10][10];
nb_mines = 20;
square = new Rect(-24, -24, 24, 24);
edge = new Rect(100, 100, 100, 100);
reset();
}
private void reset() {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
grid[i][j] = false; // CLEAR THE BOARD
if (nb_mines > 0) { // PLACE 20 MINES
Random ping = new Random();
if ((ping.nextInt(4) == 1)) {
mines[i][j] = true;
nb_mines--;
}
}
}
}
for (int i = 0; i < 10; i++) { // CALCULATES NB OF MINES AROUND
for (int j = 0; j < 10; j++) {
if (!mines[i][j]) {
nb_ard = 0;
if (i == 0 && j == 0) { // TOP LEFT
if (mines[0][1])
nb_ard++;
if (mines[1][0])
nb_ard++;
if (mines[1][1])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 0 && j > 0 && j < 9) { // 1st Column
if (mines[0][j - 1])
nb_ard++;
if (mines[0][j + 1])
nb_ard++;
if (mines[1][j])
nb_ard++;
if (mines[1][j - 1])
nb_ard++;
if (mines[1][j + 1])
nb_ard++;
arround[i][j] = nb_ard;
} else if (j == 0 && i < 9) { // 1st Row
if (mines[i - 1][0])
nb_ard++;
if (mines[i + 1][0])
nb_ard++;
if (mines[i][1])
nb_ard++;
if (mines[i - 1][1])
nb_ard++;
if (mines[i + 1][1])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 9 && j == 9) { // BOTTOM RIGHT
if (mines[8][8])
nb_ard++;
if (mines[8][9])
nb_ard++;
if (mines[9][8])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 9 && j < 9 && j > 0) { // Last Column
if (mines[9][j - 1])
nb_ard++;
if (mines[9][j + 1])
nb_ard++;
if (mines[8][j])
nb_ard++;
if (mines[8][j - 1])
nb_ard++;
if (mines[8][j + 1])
nb_ard++;
arround[i][j] = nb_ard;
} else if (j == 9 && i < 9 && i > 0) { // Last Row
if (mines[i - 1][0])
nb_ard++;
if (mines[i + 1][0])
nb_ard++;
if (mines[i][8])
nb_ard++;
if (mines[i - 1][8])
nb_ard++;
if (mines[i + 1][8])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 0 && j == 9) { // BOTTOM LEFT
if (mines[0][8])
nb_ard++;
if (mines[1][9])
nb_ard++;
if (mines[1][8])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 9 && j == 0) { // TOP RIGHT
if (mines[9][1])
nb_ard++;
if (mines[8][0])
nb_ard++;
if (mines[8][1])
nb_ard++;
arround[i][j] = nb_ard;
} else { // MIDDLE GRID
if (mines[i - 1][j - 1])
nb_ard++;
if (mines[i - 1][j])
nb_ard++;
if (mines[i - 1][j + 1])
nb_ard++;
if (mines[i][j - 1])
nb_ard++;
if (mines[i][j + 1])
nb_ard++;
if (mines[i + 1][j - 1])
nb_ard++;
if (mines[i + 1][j])
nb_ard++;
if (mines[i + 1][j + 1])
nb_ard++;
arround[i][j] = nb_ard;
}
}
}
}
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
touchy[j] = 50.f;
canvas.save();
if (i == 0 && j == 0)
canvas.translate(200.f, touchy[j]);
else
canvas.translate(touchx[i], touchy[j]);
canvas.drawRect(edge, white);
if (!grid[i][j])
canvas.drawRect(square, black);
else {
if (arround[i][j] == 1) {
canvas.drawRect(square, grey);
canvas.drawText(Integer.toString(1), i, j, blue);
} else if (arround[i][j] == 2) {
canvas.drawRect(square, grey);
canvas.drawText(Integer.toString(2), i, j, green);
} else if (arround[i][j] == 3) {
canvas.drawRect(square, grey);
canvas.drawText(Integer.toString(3), i, j, yellow);
} else if (arround[i][j] > 3) {
canvas.drawRect(square, grey);
canvas.drawText(Integer.toString(arround[i][j]), i, j,
red);
} else if (mines[i][j]) {
canvas.drawRect(square, red);
canvas.drawText("M", i, j, black);
} else if (arround[i][j] == 0)
canvas.drawRect(square, grey);
}
}
touchx[i] += 50.f;
canvas.translate(touchx[i], -10 * touchx[i]);
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
int pidX = (int) event.getX();
int pidY = (int) event.getY();
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (touchx[i] == pidX && touchy[j] == pidY) {
if (mines[i][j]) {
reset();
}
}
}
}
invalidate();
return true;
}
return super.onTouchEvent(event);
}
}
问题肯定出在 onDraw() 方法中,但正如我之前所说,我真的不知道如何以更好的方式绘制网格。希望您能理解我的问题。
我知道我有很多方法,当我找到解决问题的办法时我会解决这个问题。
提前致谢!
所以您可以考虑重构您的代码...
创建一个类似 "Tile" 的对象,而不是拥有不同的对象。它可能看起来像:
public 枚举 DrawState {
普通的,
安全的,
爆炸了;
}<br>
public class 绘画 {
public static Paint 正常,安全,爆炸;
静态的 {
NORMAL = new Paint(); NORMAL.setColor(...) 设置样式(...)
// ETC
} // 这些甚至可以是 DrawState 的成员,只需将它们传递到
// 构造函数然后你可以像 DrawState.Exploded.getPaint();
}<br>
public class 瓷砖 {
私有最终 int id;
私有矩形边界矩形;
私人布尔 hasMine;
私有 DrawState 绘制状态;
public Tile(int theId, Rect theBoundingRect, boolean mined) {
id = theId;
boundingRect = theBoundingRect;
hasMine = 已开采;
drawState = DrawState.Normal;
}<br>
public boolean containsTouch(int x, int y) {
return boundingRect.contains(x, y);
}<br>
public void draw(Canvas c) {
切换(状态){
情况正常:
c.drawRect(boundingRect, Paints.NORMAL);
return;
案例安全:
c.drawRect(boundingRect, Paints.SAFE);
return;
案例爆炸:
c.drawRect(boundingRect, Paints.EXPLODED);
}
}<br>
public void setInspected() {
drawState = DrawState.SAFE;
}<br>
public boolean isMined() { return hasMine; }
public int getId() { return id; }
// ETC
}
现在在您的自定义字段视图中,您可以执行以下操作:
@Override
public boolean onTouch(MotionEvent event) {
for (Tile t : tiles) {
if (t.containsTouch(event.getX(), event.getY())) {
if (t.isMined()) {
reset();
} else {
t.setInspected();
}
invalidate();
}
}
}
这让事情变得简单多了。当然,您必须初始化您的图块列表,您可以通过计算每个矩形的左、上、右、底并将 new Rect(l, t, r, b)
传递到图块构造函数中来实现。然后它可以很容易地获得 id,或数组位置,以及很多其他的东西......
当然,如果你想把id做成一个元组,你可以只用取模运算符;如果您知道您的图块将保留在列表中的初始索引位置,您甚至不需要保存 id。同样,您也可以在初始化图块时传入 i 和 j 而不是 id。
希望对您有所帮助!
编辑进一步解释:
所以现在由于每个图块都有足够的信息来照顾自己,它可以有一个绘制函数,例如:
public void draw(Canvas c) {
c.drawRect(boundingRect, drawState.getPaint());
// assuming the paint is set to Style.FILL it'll fill the
// rect with t/l/b/r as was set during construction
// if you had a flag image, you could draw it on top of the boundingRect
// int t = boundingRect.centerX() - (flagImage.getIntrisnicWidth()/2);
// int l = boundingRect.centerY() - (flagImage.getIntrisnicHeight()/2);
// c.drawBitmap(flagImage, bitmapTop, bitmapLeft, null);
}
现在从您的自定义网格视图 class:
@Override
public void onDraw(Canvas c) {
// draw other things
for (Tile t : tiles) {
t.draw(c);
}
// draw other things
}
记住先绘制的会被覆盖,所以如果你这样做:
canvas.drawRect(rect1);
canvas.drawRect(rect2);
rect2 与 rect1 位于同一位置,它将绘制在 rect1 之上。
我正在使用 Canvas 和我使用 canvas.drawRect 绘制的 10x10 网格创建一个扫雷器。问题是,我不知道如何获取任何单元格的坐标(例如:在我的网格上的位置 (2,3) 中,物理坐标是什么?)。也许我没有正确地做网格? (我是初学者..)但是我必须使用Canvas,没有图形。我在这里看到了一些答案,但没有真正帮助我..
代码如下:
package com.example.meinsweeper_ja;
import java.util.Random;
import android.annotation.SuppressLint;
import android.content.*;
import android.graphics.*;
import android.util.*;
import android.view.*;
public class CustomView extends View {
private Paint black, grey, white, blue, green, yellow, red;
private Rect square, edge;
private boolean grid[][]; // true = uncovered false = covered
private boolean mines[][]; // true = mine false = no mine
private int arround[][]; // Nb of mine around
private float touchx[];
private float touchy[];
private int nb_mines, nb_ard;
public CustomView(Context c) {
super(c);
init();
}
public CustomView(Context c, AttributeSet as) {
super(c, as);
init();
}
public CustomView(Context c, AttributeSet as, int default_style) {
super(c, as, default_style);
init();
}
private void init() {
black = new Paint(Paint.ANTI_ALIAS_FLAG);
grey = new Paint(Paint.ANTI_ALIAS_FLAG);
white = new Paint(Paint.ANTI_ALIAS_FLAG);
yellow = new Paint(Paint.ANTI_ALIAS_FLAG);
red = new Paint(Paint.ANTI_ALIAS_FLAG);
green = new Paint(Paint.ANTI_ALIAS_FLAG);
blue = new Paint(Paint.ANTI_ALIAS_FLAG);
black.setColor(0xFF000000);
grey.setColor(0xFF808080);
white.setColor(0xFFFFFFFF);
yellow.setColor(0xFFFFFF00);
red.setColor(0xFFFF0000);
green.setColor(0xFF00FF00);
blue.setColor(0xFF0000FF);
touchx = new float[10];
touchy = new float[10];
grid = new boolean[10][10];
mines = new boolean[10][10];
arround = new int[10][10];
nb_mines = 20;
square = new Rect(-24, -24, 24, 24);
edge = new Rect(100, 100, 100, 100);
reset();
}
private void reset() {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
grid[i][j] = false; // CLEAR THE BOARD
if (nb_mines > 0) { // PLACE 20 MINES
Random ping = new Random();
if ((ping.nextInt(4) == 1)) {
mines[i][j] = true;
nb_mines--;
}
}
}
}
for (int i = 0; i < 10; i++) { // CALCULATES NB OF MINES AROUND
for (int j = 0; j < 10; j++) {
if (!mines[i][j]) {
nb_ard = 0;
if (i == 0 && j == 0) { // TOP LEFT
if (mines[0][1])
nb_ard++;
if (mines[1][0])
nb_ard++;
if (mines[1][1])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 0 && j > 0 && j < 9) { // 1st Column
if (mines[0][j - 1])
nb_ard++;
if (mines[0][j + 1])
nb_ard++;
if (mines[1][j])
nb_ard++;
if (mines[1][j - 1])
nb_ard++;
if (mines[1][j + 1])
nb_ard++;
arround[i][j] = nb_ard;
} else if (j == 0 && i < 9) { // 1st Row
if (mines[i - 1][0])
nb_ard++;
if (mines[i + 1][0])
nb_ard++;
if (mines[i][1])
nb_ard++;
if (mines[i - 1][1])
nb_ard++;
if (mines[i + 1][1])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 9 && j == 9) { // BOTTOM RIGHT
if (mines[8][8])
nb_ard++;
if (mines[8][9])
nb_ard++;
if (mines[9][8])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 9 && j < 9 && j > 0) { // Last Column
if (mines[9][j - 1])
nb_ard++;
if (mines[9][j + 1])
nb_ard++;
if (mines[8][j])
nb_ard++;
if (mines[8][j - 1])
nb_ard++;
if (mines[8][j + 1])
nb_ard++;
arround[i][j] = nb_ard;
} else if (j == 9 && i < 9 && i > 0) { // Last Row
if (mines[i - 1][0])
nb_ard++;
if (mines[i + 1][0])
nb_ard++;
if (mines[i][8])
nb_ard++;
if (mines[i - 1][8])
nb_ard++;
if (mines[i + 1][8])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 0 && j == 9) { // BOTTOM LEFT
if (mines[0][8])
nb_ard++;
if (mines[1][9])
nb_ard++;
if (mines[1][8])
nb_ard++;
arround[i][j] = nb_ard;
} else if (i == 9 && j == 0) { // TOP RIGHT
if (mines[9][1])
nb_ard++;
if (mines[8][0])
nb_ard++;
if (mines[8][1])
nb_ard++;
arround[i][j] = nb_ard;
} else { // MIDDLE GRID
if (mines[i - 1][j - 1])
nb_ard++;
if (mines[i - 1][j])
nb_ard++;
if (mines[i - 1][j + 1])
nb_ard++;
if (mines[i][j - 1])
nb_ard++;
if (mines[i][j + 1])
nb_ard++;
if (mines[i + 1][j - 1])
nb_ard++;
if (mines[i + 1][j])
nb_ard++;
if (mines[i + 1][j + 1])
nb_ard++;
arround[i][j] = nb_ard;
}
}
}
}
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
touchy[j] = 50.f;
canvas.save();
if (i == 0 && j == 0)
canvas.translate(200.f, touchy[j]);
else
canvas.translate(touchx[i], touchy[j]);
canvas.drawRect(edge, white);
if (!grid[i][j])
canvas.drawRect(square, black);
else {
if (arround[i][j] == 1) {
canvas.drawRect(square, grey);
canvas.drawText(Integer.toString(1), i, j, blue);
} else if (arround[i][j] == 2) {
canvas.drawRect(square, grey);
canvas.drawText(Integer.toString(2), i, j, green);
} else if (arround[i][j] == 3) {
canvas.drawRect(square, grey);
canvas.drawText(Integer.toString(3), i, j, yellow);
} else if (arround[i][j] > 3) {
canvas.drawRect(square, grey);
canvas.drawText(Integer.toString(arround[i][j]), i, j,
red);
} else if (mines[i][j]) {
canvas.drawRect(square, red);
canvas.drawText("M", i, j, black);
} else if (arround[i][j] == 0)
canvas.drawRect(square, grey);
}
}
touchx[i] += 50.f;
canvas.translate(touchx[i], -10 * touchx[i]);
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
int pidX = (int) event.getX();
int pidY = (int) event.getY();
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (touchx[i] == pidX && touchy[j] == pidY) {
if (mines[i][j]) {
reset();
}
}
}
}
invalidate();
return true;
}
return super.onTouchEvent(event);
}
}
问题肯定出在 onDraw() 方法中,但正如我之前所说,我真的不知道如何以更好的方式绘制网格。希望您能理解我的问题。
我知道我有很多方法,当我找到解决问题的办法时我会解决这个问题。
提前致谢!
所以您可以考虑重构您的代码...
创建一个类似 "Tile" 的对象,而不是拥有不同的对象。它可能看起来像:
public 枚举 DrawState {
普通的,
安全的,
爆炸了;
}<br>
public class 绘画 {
public static Paint 正常,安全,爆炸;
静态的 {
NORMAL = new Paint(); NORMAL.setColor(...) 设置样式(...)
// ETC
} // 这些甚至可以是 DrawState 的成员,只需将它们传递到
// 构造函数然后你可以像 DrawState.Exploded.getPaint();
}<br>
public class 瓷砖 {
私有最终 int id;
私有矩形边界矩形;
私人布尔 hasMine;
私有 DrawState 绘制状态;
public Tile(int theId, Rect theBoundingRect, boolean mined) {
id = theId;
boundingRect = theBoundingRect;
hasMine = 已开采;
drawState = DrawState.Normal;
}<br>
public boolean containsTouch(int x, int y) {
return boundingRect.contains(x, y);
}<br>
public void draw(Canvas c) {
切换(状态){
情况正常:
c.drawRect(boundingRect, Paints.NORMAL);
return;
案例安全:
c.drawRect(boundingRect, Paints.SAFE);
return;
案例爆炸:
c.drawRect(boundingRect, Paints.EXPLODED);
}
}<br>
public void setInspected() {
drawState = DrawState.SAFE;
}<br>
public boolean isMined() { return hasMine; }
public int getId() { return id; }
// ETC
}
现在在您的自定义字段视图中,您可以执行以下操作:
@Override
public boolean onTouch(MotionEvent event) {
for (Tile t : tiles) {
if (t.containsTouch(event.getX(), event.getY())) {
if (t.isMined()) {
reset();
} else {
t.setInspected();
}
invalidate();
}
}
}
这让事情变得简单多了。当然,您必须初始化您的图块列表,您可以通过计算每个矩形的左、上、右、底并将 new Rect(l, t, r, b)
传递到图块构造函数中来实现。然后它可以很容易地获得 id,或数组位置,以及很多其他的东西......
当然,如果你想把id做成一个元组,你可以只用取模运算符;如果您知道您的图块将保留在列表中的初始索引位置,您甚至不需要保存 id。同样,您也可以在初始化图块时传入 i 和 j 而不是 id。
希望对您有所帮助!
编辑进一步解释:
所以现在由于每个图块都有足够的信息来照顾自己,它可以有一个绘制函数,例如:
public void draw(Canvas c) {
c.drawRect(boundingRect, drawState.getPaint());
// assuming the paint is set to Style.FILL it'll fill the
// rect with t/l/b/r as was set during construction
// if you had a flag image, you could draw it on top of the boundingRect
// int t = boundingRect.centerX() - (flagImage.getIntrisnicWidth()/2);
// int l = boundingRect.centerY() - (flagImage.getIntrisnicHeight()/2);
// c.drawBitmap(flagImage, bitmapTop, bitmapLeft, null);
}
现在从您的自定义网格视图 class:
@Override
public void onDraw(Canvas c) {
// draw other things
for (Tile t : tiles) {
t.draw(c);
}
// draw other things
}
记住先绘制的会被覆盖,所以如果你这样做:
canvas.drawRect(rect1);
canvas.drawRect(rect2);
rect2 与 rect1 位于同一位置,它将绘制在 rect1 之上。