Android 自定义矩形绘图

Android Custom Rectangle Drawing

我想在 Android UI 中创建拼字板类型的视图。为此,我目前决定(仍在尝试最好的方法)使用带有自定义 BoardCell 视图的 TableLayout 和 TableRow 视图作为面板的各个单元格。

我还是 Android 的新手,我以前从未创建过自定义视图。我 运行 遇到了绘制矩形的问题。我可以绘制一行的第一个单元格,但无法绘制该行中剩余的 14 个单元格。在调试期间,所有 BoardCell 对象都被初始化,并且每个对象都被调用 'onDraw'。测量值和左、上、右和下位置似乎也是正确的。所以我的问题是......我做错了什么?那一定是我想念的简单东西?

我试过简单地绘制一些单元格的笔画(如下面的代码所示),但右侧和底部的线条丢失了。我知道这与它有关,但我不明白为什么单元格的右侧和底部不会被绘制?

以下是所有相关代码,删除无关代码以缩短此 post。

activity_game.xml

<!-- Game Board --> <TableLayout android:id="@+id/board_grid" android:layout_width="355dp" android:layout_height="355dp" android:layout_below="@+id/score_container" android:layout_marginRight="2dp" android:layout_marginLeft="2dp" android:background="@color/white" android:stretchColumns="*"> <!-- Row 1 --> <TableRow android:layout_weight="1"> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp" custom:cellType="doubleWord"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp" custom:cellType="doubleLetter"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp" custom:cellType="doubleLetter"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp"/> <BoardCellView android:layout_height="21dp" android:layout_width="0dp" android:layout_margin="1dp" custom:cellType="doubleWord"/> </TableRow> </TableLayout>

BoardCellView.java

public class BoardCellView extends View {

    private static final String TAG = BoardCellView.class.getName();

    private static final int STANDARD_CELL_COLOUR       = Color.rgb(234, 234, 234);
    private static final int DOUBLE_LETTER_CELL_COLOUR  = Color.rgb(200, 175, 224);
    private static final int TRIPLE_LETTER_CELL_COLOUR  = Color.rgb(132, 217, 168);
    private static final int DOUBLE_WORD_CELL_COLOUR    = Color.rgb(255, 181, 131);
    private static final int TRIPLE_WORD_CELL_COLOUR    = Color.rgb(146, 205, 251);

    private Paint standardCellPaint;
    private Paint doubleLetterPaint;
    private Paint tripleLetterPaint;
    private Paint doubleWordPaint;
    private Paint tripleWordPaint;

    private CellType cellType;

    private Rect cellRectangle;

    public BoardCellView(Context context, AttributeSet attrs) {
        super(context, attrs);

        init();

        TypedArray typedArray = context.getTheme().obtainStyledAttributes(
            attrs,
            R.styleable.BoardCellView,
            0, 0);

        try {
            this.cellType =     CellType.fromInt(typedArray.getInt(R.styleable.BoardCellView_cellType, 0));

        } finally {
            typedArray.recycle();
        }
    }

    /**
     * Setup the paint and rectangle objects
     */
    private void init() {
        // The rectangle we will use to draw
        this.cellRectangle = new Rect();

        // Setup the Paints for each cell type
        this.standardCellPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        this.standardCellPaint.setStyle(Paint.Style.STROKE);
        this.standardCellPaint.setColor(STANDARD_CELL_COLOUR);
        this.standardCellPaint.setStrokeWidth(5.0f);

        this.doubleLetterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        this.doubleLetterPaint.setStyle(Paint.Style.FILL);
        this.doubleLetterPaint.setColor(DOUBLE_LETTER_CELL_COLOUR);

        this.tripleLetterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        this.tripleLetterPaint.setStyle(Paint.Style.FILL);
        this.tripleLetterPaint.setColor(TRIPLE_LETTER_CELL_COLOUR);

        this.doubleWordPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        this.doubleWordPaint.setStyle(Paint.Style.STROKE);
        this.doubleWordPaint.setColor(DOUBLE_WORD_CELL_COLOUR);
        this.doubleWordPaint.setStrokeWidth(5.0f);

        this.tripleWordPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        this.tripleWordPaint.setStyle(Paint.Style.FILL);
        this.tripleWordPaint.setColor(TRIPLE_WORD_CELL_COLOUR);

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        this.cellRectangle.set(left, top, right, bottom);
        Log.d(TAG, "---- " + left + ":"+ top + ":"+ right + ":"+ bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // Standard cell first as this is by far the most common
        switch (this.cellType) {
            case STANDARD:
                Log.d(TAG, "---- Drawing Standard: " + this.cellRectangle.toShortString());
                canvas.drawRect(this.cellRectangle, this.standardCellPaint);
                break;
            case DL:
                Log.d(TAG, "---- Drawing DL");
                canvas.drawRect(this.cellRectangle, this.doubleLetterPaint);
                break;
            case TL:
                Log.d(TAG, "---- Drawing TL");
                canvas.drawRect(this.cellRectangle, this.tripleLetterPaint);
                break;
            case DW:
                Log.d(TAG, "---- Drawing DW: " + this.cellRectangle.toShortString());
                canvas.drawRect(this.cellRectangle, this.doubleWordPaint);
                break;
            case TW:
                Log.d(TAG, "---- Drawing TW");
                canvas.drawRect(this.cellRectangle, this.tripleWordPaint);
                break;
        }
    }
}

从Logcat输出(第一个是onLayout的输出,第二个是onDraw的输出)

BoardCellView﹕ ---- 3:3:68:66
03-27 10:57:12.534  27810-27810/app D/BoardCellView﹕ ---- 74:3:139:66
03-27 10:57:12.534  27810-27810/app D/BoardCellView﹕ ---- 145:3:210:66
03-27 10:57:12.534  27810-27810/app D/BoardCellView﹕ ---- 216:3:281:66
03-27 10:57:12.534  27810-27810/app D/BoardCellView﹕ ---- 287:3:352:66
03-27 10:57:12.536  27810-27810/app D/BoardCellView﹕ ---- 358:3:423:66
....
---- Drawing DW: [3,3][68,66]
03-27 10:57:12.576  27810-27810/app D/BoardCellView﹕ ---- Drawing Standard: [74,3][139,66]
03-27 10:57:12.576  27810-27810/app D/BoardCellView﹕ ---- Drawing Standard: [145,3][210,66]
03-27 10:57:12.576  27810-27810/app D/BoardCellView﹕ ---- Drawing Standard: [216,3][281,66]
03-27 10:57:12.576  27810-27810/app D/BoardCellView﹕ ---- Drawing Standard: [287,3][352,66]

下面是视图的绘制方式。前 2 行使用我的自定义视图。其余行使用具有背景颜色的简单视图对象。

![看板]http://cl.ly/image/0T2F3i1l3y24

我花了几天时间尝试 Google 和此处 (Drawing multiple custom views) 的各种解决方案,但我没有找到任何解决我问题的方法。预先感谢您的帮助。

问题出在 onLayout()cellRectangle 的设置。传入 onLayout() 的矩形坐标是相对于父视图的,但是调用 drawRect() 时需要在 onDraw() 中使用的坐标应该是相对于视图本身的,因为设置 Canvas 使得视图的左上角对应于 (0, 0).

因此父视图中的 (left, top) 将对应于子视图中的 (0, 0),并且 (right, bottom) 将对应于 (right-left, bottom-top) .基本上你只需要从它们中减去 (left, top) 即可。

考虑到这一点,像这样更改 onLayout

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    this.cellRectangle.set(0, 0, right-left, bottom-top);
    Log.d(TAG, "---- " + left + ":"+ top + ":"+ right + ":"+ bottom);
}