Search code examples
androidandroid-canvasandroid-custom-viewondrawabstract-algebra

Use Canvas to draw thousands of Rects


I am trying to make a native Android version of http://arapaho.nsuok.edu/~deckar01/Zvis.html

So, I made a custom View that draws all the squares needed. Of course, this drawing ends up taking 10s of seconds once the number becomes large enough to make the Canvas start drawing thousands of squares.

Is there a better way to do this? It seems like there is something obvious I am not thinking of doing/using.

The onDraw method of the View is below, in case that helps. Any ideas?

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

    final int number = mNumber;
    final float tileWidth, tileHeight;

    /*mTileWidth = mWW / (number - 1);
    mTileHeight = mHH / (number - 1);*/

    // make them squares
    if (mWW <= mHH) {
        tileWidth = tileHeight = mWW / (number - 1);
    } else {
        tileWidth = tileHeight = mHH / (number - 1);
    }

    mWhiteTextPaint.setTextSize(48f / 72 * tileWidth);
    mBlackTextPaint.setTextSize(48f / 72 * tileWidth);

    float currX = getPaddingLeft();
    float currY = getPaddingTop();

    for (int i = 1; i <= number - 1; i++) {
        mBackgroundPaint.setColor(getBackgroundColor(i, number));
        canvas.drawRect(currX, currY, currX + tileWidth,
                currY + tileHeight,
                mBackgroundPaint);
        final String text = String.valueOf(i);
        canvas.drawText(text,
                currX + tileWidth / 2 - mWhiteTextPaint.measureText(text) / 2,
                currY + tileHeight * 0.9f, mWhiteTextPaint);
        currX += tileWidth;
    }
    currX = getPaddingLeft();
    currY += tileHeight;

    for (int i = 2; i <= number - 1; i++) {
        for (int j = 1; j <= number - 1; j++) {
            final int num = (j == 1) ? i : (i * j) % number;

            mBackgroundPaint.setColor(getBackgroundColor(num, number));
            canvas.drawRect(currX, currY, currX + tileWidth,
                    currY + tileHeight,
                    mBackgroundPaint);
            final String text = String.valueOf(num);
            if (num == 0) {
                canvas.drawText(text,
                        currX + tileWidth / 2 - mBlackTextPaint.measureText(text) / 2,
                        currY + tileHeight * 0.9f, mBlackTextPaint);
            } else {
                canvas.drawText(text,
                        currX + tileWidth / 2 - mWhiteTextPaint.measureText(text) / 2,
                        currY + tileHeight * 0.9f, mWhiteTextPaint);
            }
            currX += tileWidth;
        }
        currX = getPaddingLeft();
        currY += tileHeight;
    }

    if (mOnDrawFinishedListener != null) {
        mOnDrawFinishedListener.onDrawFinished(number);
    }
}

Solution

  • As @CarCzar already said, you could draw everything in a separate thread into a bitmap and then on the UI thread you only draw that bitmap on the screen. Alternatively you could use OpenGL. That's usually used for more dynamic things like games. The thing is that OpenGL runs in a separate graphic thread and therefore will not block your UI.