Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas drawing not being drawn properly despite setting properties

I've just created 2 drawings in an attempt to get one to look the same as the other but I'm having no luck. As regards to the screenshots, the drawing on the top was created using canvas and the drawing on the bottom was created using XML. The XML drawing is prefect for me but unfortunately according to Android, I shouldn't use it as it returns the 'too many views' warning. I've tried the following methods for the canvas drawing but I'm still not getting the desired result.

I've tested the project on a dozen emulators and they all take no more than a second to appear despite the XML drawing having so many views.

  1. Using different drawings based on the device's orientation
  2. Using float values rather than integers
  3. Adjusting the float values numerous times

Does anyone know what other options there are to resolve this annoying canvas issue and also ensure that the canvas drawing looks exactly the same as the XML drawing regardless of screen size and orientation? Also should the 'too many views' warning be ignored in this scenario and/or I increase the view count limit to above 80? All relevant help would be highly appreciated.

Java code

http://pastebin.com/VXgkJR2Z

XML code

http://pastebin.com/JyVvxS5n

enter image description here enter image description here

like image 881
wbk727 Avatar asked Sep 01 '15 18:09

wbk727


1 Answers

Below you can see results after working on your code. It is almost similar to the xml, but if you look at it with mignifier you can find some differencies.

So there are some important things when you are drawing.

  1. Don't use int. You are loosing precision. I replaced all int to float in your code.
  2. Be careful in loops. Your are loosing precision in loops too.

Common pattern in your code:

float boxWidth = (getWidth() - mSideRectWidth) / 8; // I replaced int to float
for (int i = 0; i < 8; i++) {
    float position = i * boxWidth; // loss of precision
    ...
}

It is better to calculate position in loop:

for (int i = 0; i < 8; i++) {
    float position = i * (getWidth() - mSideRectWidth) / 8;
    ...
}
  1. Don't forget the stroke width. You miss that value when calculate positions of shapes and lines.

And here is my full code:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

public class Car extends View {
    private final Paint mBlackPaint = new Paint();
    private final Paint mRedPaint = new Paint();
    private final TextPaint mTextPaint;

    public static final int BOXES_COUNT = 8;

    private float oneDp;
    private float textSize;
    private float windowHeight;

    public Car(Context context, AttributeSet attrs) {
        super(context, attrs);
        oneDp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
            getResources().getDisplayMetrics());
        windowHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10,
            getResources().getDisplayMetrics());
        textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 15,
            getResources().getDisplayMetrics());

        mRedPaint.setColor(Color.parseColor("#CC3333"));

        mBlackPaint.setAntiAlias(true);
        mBlackPaint.setColor(Color.BLACK);
        mBlackPaint.setStrokeWidth(oneDp);
        mBlackPaint.setStyle(Paint.Style.STROKE);

        mTextPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
        mTextPaint.setColor(Color.BLACK);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setTextSize(textSize);

        mWindowPaint = new Paint();
        mWindowPaint.setAntiAlias(true);
        mWindowPaint.setColor(Color.parseColor("#CC3333"));
        mWindowPaint.setStyle(Paint.Style.STROKE);
        mWindowPaint.setStrokeWidth(oneDp);
    }

    private Paint mWindowPaint;
    RectF rect = new RectF();
    RectF rect2 = new RectF();

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

        if (getWidth() == 0) return;

        int w = canvas.getWidth();
        int h = canvas.getHeight();

        //draw red rectangles
        float mSideRectWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5,
            getResources().getDisplayMetrics());
        canvas.drawRect(0, 0, mSideRectWidth, getHeight(), mRedPaint); //draw left end rectangle
        canvas.drawRect(getWidth() - mSideRectWidth, 0, getWidth(), getHeight(),
            mRedPaint); //draw right end rectangle

        //draw grey boxes
        setBackgroundColor(Color.parseColor("#808080"));

        for (int i = 0; i < BOXES_COUNT; i++) {
            float leftPosition = mSideRectWidth
                + i * oneDp
                + (getWidth() - mSideRectWidth * 2 - (BOXES_COUNT - 1) * oneDp) * i / BOXES_COUNT;
            float rightPosition = mSideRectWidth
                + i * oneDp
                + (getWidth() - mSideRectWidth * 2 - (BOXES_COUNT - 1) * oneDp) * (i + 1)
                / BOXES_COUNT;
            if (i == 0) {
                fillRectLeft(canvas, leftPosition, rightPosition, (i + 1) + "");
            } else if ( i == BOXES_COUNT - 1) {
                fillRectRight(canvas, leftPosition, rightPosition, (i + 1) + "");
            } else {
                fillRect(canvas, leftPosition, rightPosition, (i + 1) + "");
            }
        }

        //draw black lines
        for (int i = 1; i < BOXES_COUNT; i++) {
            float position = mSideRectWidth + (getWidth() - mSideRectWidth * 2) * i / BOXES_COUNT;
            canvas.drawLine(position, 0, position, getHeight(), mBlackPaint);
        }
    }

    private void fillRect(Canvas canvas, float left, float right, String number) {
        rect.set(left + oneDp / 2, 0 + oneDp / 2, right - oneDp / 2, getHeight() - oneDp / 2);
        float xPos = left + ((right - left) / 2);
        float yPos = (canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2);
        //((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
        canvas.drawText(number, xPos, yPos, mTextPaint);
        //canvas.drawRect(rect, mWindowPaint);

        // top row
        rect2.set(left + oneDp / 2, 0 + oneDp / 2, left + (right - left) * 20 / 100 - oneDp / 2,
            windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 27 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 47 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 53 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 73 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 80 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 100 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);

        // bottom row
        rect2.set(left + oneDp / 2, getHeight() - windowHeight + oneDp / 2,
            left + (right - left) * 20 / 100 - oneDp / 2, getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 27 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 47 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 53 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 73 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 80 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 100 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
    }

    private void fillRectLeft(Canvas canvas, float left, float right, String number) {
        rect.set(left + oneDp / 2, 0 + oneDp / 2, right - oneDp / 2, getHeight() - oneDp / 2);
        float xPos = left + ((right - left) / 2);
        float yPos = (canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2);
        //((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
        canvas.drawText(number, xPos, yPos, mTextPaint);
        //canvas.drawRect(rect, mWindowPaint);

        // top row
        rect2.set(left + (right - left) * 4 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 24 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 42 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 62 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 80 / 100 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 100 / 100 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);

        // bottom row
        rect2.set(left + (right - left) * 4 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 24 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 42 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 62 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 80 / 100 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 100 / 100 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
    }

    private void fillRectRight(Canvas canvas, float left, float right, String number) {
        rect.set(left + oneDp / 2, 0 + oneDp / 2, right - oneDp / 2, getHeight() - oneDp / 2);
        float xPos = left + ((right - left) / 2);
        float yPos = (canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2);
        //((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.
        canvas.drawText(number, xPos, yPos, mTextPaint);
        //canvas.drawRect(rect, mWindowPaint);

        // top row
        rect2.set(left + (right - left) * 0 / 105 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 20 / 105 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 38 / 105 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 58 / 105 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 78 / 105 + oneDp / 2, 0 + oneDp / 2,
            left + (right - left) * 98 / 105 - oneDp / 2, windowHeight - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);

        // bottom row
        rect2.set(left + (right - left) * 0 / 105 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 20 / 105 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 38 / 105 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 58 / 105 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
        rect2.set(left + (right - left) * 78 / 105 + oneDp / 2,
            getHeight() - windowHeight + oneDp / 2, left + (right - left) * 98 / 105 - oneDp / 2,
            getHeight() - oneDp / 2);
        canvas.drawRect(rect2, mWindowPaint);
    }
}

Portrait Portrait

Landscape Landscape

like image 151
Ilya Tretyakov Avatar answered Nov 05 '22 17:11

Ilya Tretyakov