android - 如何快速、一致地调整多个 Drawable 的大小并更新它们的边界

标签 android android-layout user-interface 2d-games

我在实现基于六边形网格的游戏时遇到问题。因为游戏需要一个 91 单元格的六角板,就像我下面的简单板一样,我希望用户能够通过缩放/捏合来缩放板,并通过平移来移动它。但是,我的缩放和移动实现都不允许我同时拥有:

1) 一致的边界,让我能够将 fragment 拖放到构成每个单元格的 Drawable 上。

2) 一致的相对定位,允许单元格在下面的相同配置中保持彼此相邻。

3) 平滑的缩放和平移,让游戏快速运行并拥有与其他应用类似的缩放/捏合体验。

A 91-cell hex board

我尝试过的一些事情(都是使用 Activity -> SurfaceView -> Thread 完成的,就像在 LunarLander 示例中一样):

  1. 绘制到位图,使用矩阵缩放位图,平移 Canvas 。处理第 2 点和第 3 点,但我不知道如何使单元格的边界保持一致。 HexCell 是一个包含单个单元格的 Drawable 的类,二维数组 board 包含 HexCells。

    public void drawBoard(Canvas c, int posX, int posY, float scaleFactor, float pivotPointX, float pivotPointY, boolean firstDraw) {
        for(int i = 0; i < board.size(); i++) {
            for(int j = 0; j < board.get(i).size(); j++) {
                board.get(i).get(j).draw(bitmapCanvas);
            }
        }
        if(firstDraw) {
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();
            float scale;
            if(canvasWidth < canvasHeight) {
                scale = ((float) canvasWidth) / width;
            }
            else {
                scale = ((float) canvasHeight) / height;
            }
            Matrix matrix = new Matrix();
    
            // Resize the bit map
                matrix.postScale(scale, scale);
    
            // Recreate the new Bitmap
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
            c.drawBitmap(bitmap, matrix, null);
        }
        c.save();
        c.translate(posX, posY);
        Matrix matrix = new Matrix();
        matrix.postScale(scaleFactor, scaleFactor, pivotPointX, pivotPointY);
        c.drawBitmap(bitmap, matrix, null);
        c.restore();
    }
    
  2. 使用矩阵修改可绘制对象的边界。这更新了点 1 的边界,并且通过一些调整我认为我可以获得 2 和 3,但我不确定如何使缩放停止“粘性”,这意味着不像第一种方法那样平滑。此外,当对单元格进行大量缩放时,其中一些单元格会变得不同大小并开始相对于其他单元格移动位置。

    public void drawBoard(Canvas c, int posX, int posY, float scaleFactor, float pivotPointX, float pivotPointY, boolean firstDraw) {
        for(int i = 0; i < board.size(); i++) {
            for(int j = 0; j < board.get(i).size(); j++) {
                Rect bounds = board.get(i).get(j).getBounds();
                RectF boundsF = new RectF(bounds.left, bounds.top, bounds.right, bounds.bottom);
                matrix.mapRect(boundsF);
                bounds = new Rect((int)boundsF.left, (int)boundsF.top, (int)boundsF.right, (int)boundsF.bottom);
                board.get(i).get(j).setBounds(bounds);
                board.get(i).get(j).draw(c);
            }
        }
    }
    
  3. 直接修改 Drawable 的边界。这更新了第 1 点的边界,但缺少第 2 点和第 3 点。此外,因为我没有将矩阵 postScale 与 pivotPoints 一起使用,所以单元格保持在它们所在的位置的中心,并且变得更小/更大,而不会移动到每个旁边其他。

    public void resize(int dx, int dy, float scaleFactor) {
        xPos += dx;
        yPos += dy;
        width *= scaleFactor;
        height *= scaleFactor;
        cell.setBounds(xPos, yPos, xPos + width, yPos + height);
    }
    

我该怎么办?如何在缩放和移动时更新边界,以便最终将棋子放在棋盘上?我是否应该放弃缩放和平移的愿望,而是使用 GridView 或类似的东西来实现面板?


编辑:

经过进一步研究,我确定选项 1 是最佳选择。它要快得多,并使细胞保持一致的形态。我发现如果将应用于 Canvas 的转换反转并将其应用于触摸事件的坐标,则可以返回到单元格的原始边界,因此可以适本地选择它们。

但是,我无法准确反转转换。

x /= scaleFactor;
y /= scaleFactor;
x -= posX + pivotPointX;
y -= posY + pivotPointY;

不是以下的工作反转:

canvas.save();
canvas.translate(posX, posY);
canvas.scale(scaleFactor, scaleFactor, pivotPointX, pivotPointY);

有谁知道如何适本地反转它?

最佳答案

在 drawBoard() 方法中我做了:

canvas.save();
canvas.translate(posX, posY);
canvas.scale(scaleFactor, scaleFactor, pivotPointX, pivotPointY);
canvas.getMatrix(canvasMatrix); // Save the matrix that has the transformations so we can invert it
for(int i = 0; i < board.size(); i++) {
    for(int j = 0; j < board.get(i).size(); j++) {
         board.get(i).get(j).draw(c);
    }
}
canvas.restore();

在 View 的 onTouchEvent(MotionEvent ev) 方法中我做了:

float x = ev.getX();
float y = ev.getY();

float[] pts = {x, y};
Matrix canvasMatrix = boardThread.getCanvasMatrix(); // get the matrix with the transformations
Matrix invertMatrix = new Matrix();
canvasMatrix.invert(invertMatrix); // invert the matrix
invertMatrix.mapPoints(pts); // map the inversion to the points x and y
boardThread.selectHex((int)pts[0], (int)pts[1]);

这就是诀窍!现在,如果有人对避免使用已弃用的 canvas.getMatrix() 方法有任何建议,那就太好了 :)

关于android - 如何快速、一致地调整多个 Drawable 的大小并更新它们的边界,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17800258/

相关文章:

android - 当 Android View 在应用程序中可见时是否会触发任何事件?

python - 有效使用 ttk (tk 8.5) Notebook 小部件(选项卡滚动)

c++ - QTextEdit::setPalette 不更新文本颜色

android - Fragment 上的 SetSupportActionBar 未设置 SupportActionBar

c# - 单声道不能消费触摸事件

java - 创建一个 android ffmpeg 可执行文件

java - HttpUrlConnection 到 HttpsUrlConnection,我可以只添加 's' 吗?

java - 在将我的相对布局更改为 Linearlayout 时,它在 TTS 的 textview 上抛出空指针异常

ios - 不确定如何模仿所需的 UI 功能

java - 当我运行 Flutter 项目时,我的 Ubuntu 系统出现此错误