Canvas 'drawLine и drawRect не включая конечную позицию? - PullRequest
14 голосов
/ 23 января 2012

К моему удивлению, я только что обнаружил, что drawLine и drawRect не включают конечную позицию, т. Е .:

canvas.drawLine(100, 100, 100, 100, paint);

или

RectF rect = new RectF(100, 100, 100, 100);
canvas.drawRect(rect, paint);

не будет ничего рисовать.

Моя краска определяется следующим образом:

Paint paint = new Paint();
paint.setAntiAlias(false);
paint.setStyle(Paint.Style.FILL);
return paint;

Я пытался определить мою краску как FILL_AND_STROKE, но это не поможет.

Javadoc Android drawPaint () даже не перечисляет параметры stopX и stopY!

Итак, если я хочу нарисовать вертикальную линию, которая идет точно от начала к концу (включительно), я должен сделать следующее:

canvas.drawLine(constX, beginY, constX, endY + 1)

Обратите внимание, что я не добавил 1 в конечную позицию X, только в конец Y (x остается таким же, как я хочу вертикальную линию).

Мое устройство - HTC SENSE.

Редактировать: Саймон, вы правы, вместо того, чтобы задавать вопрос, я просто попытался поделиться своим удивлением, что Android не делает то, что говорят его документы, в таком фундаментальном случае, как базовое рисование, и убедитесь, что Я не делал глупых ошибок на своем пути.

Чтобы прояснить ситуацию: Javadoc от drawRect говорит:

public void drawRect (поплавок слева, поплавок сверху, поплавок справа, поплавок снизу, краска Paint)

Нарисуйте указанный Rect, используя указанную краску. Прямоугольник будет заполнен или обрамлен на основе стиля в краске.

left - Левая сторона прямоугольника, который будет нарисован

top - верхняя сторона прямоугольника, который будет нарисован

вправо - правая сторона прямоугольника, который будет нарисован

bottom - Нижняя сторона прямоугольника для рисования

paint - краска, используемая для рисования прямоугольника

Итак, при написании

canvas.drawRect(x1, y1, x2, y2)

Вы ожидаете прямоугольник, углы которого находятся в (x1, y1); (х1, у2); (x2, y1) и (x2, y2).

Android говорит: неправильно! Они будут в (x1, y1); (х1, у2-1); (x2-1, y1) и (x2-1, y2-1).

Для любопытных: установите обрезку холста:

canvas.clipRect(x1, y1, x2, y2)

Затем попробуйте нарисовать точку:

canvas.drawPoint(x1, y1, paint);

и вы получите точку на экране.

Затем попробуйте в противоположном углу:

canvas.drawPoint(x2, y2, paint);

ничего не появляется. ничего не появится и в оставшихся 2-х углах:

canvas.drawPoint(x1, y2, paint);


canvas.drawPoint(x2, y2, paint);

Все еще не удивляет вас, ребята?

Таким образом, вывод состоит в том, что Android обрабатывает правые и нижние координаты как исключительные, то есть, например, при записи:

canvas.clipRect(x1, y1, x2, y2)

Вы получите ограничения отсечения (x1, y1, x2 - 1, y2 - 1). То же самое касается каждого метода, который принимает вправо и низ координаты или Rect / RectF объекты.

Ответы [ 4 ]

5 голосов
/ 04 июля 2014

Ваш вопрос обнаруживает несоответствие в API рисования Android. Вы говорите

Итак, если я хочу нарисовать вертикальную линию, которая идет точно от начала к концу (включительно), я должен сделать следующее:

canvas.drawLine(constX, beginY, constX, endY + 1)

Обратите внимание, что я не добавил 1 в конечную позицию X, только в конец Y (x остается таким же, как я хочу вертикальную линию).

Предложение в скобках, по моему мнению, является ключом к пониманию природы несоответствия:

Вы также можете добавить 1 к конечной позиции X (или даже к начальной позиции X!), И вы получите точно одинаковую по пикселям линию. Это почему? Поскольку основной алгоритм преобразования из концепции Android «левый пиксель в / правый пиксель» в концепцию «начальный и конечный пиксель в» выглядит следующим образом (показан только для x, для y это одно и то же):

int left, top, right, bottom; // left/top pixel inclusive, right/bottom pixel exclusive
int x1, y1, x2, y2;           // x1/y1 and x2/y2 pixels inclusive

if ( left == right ) {
    x1 = x2 = left;
} else if ( left < right ) {
    x1 = left;
    x2 = right - 1;
} else {
    x1 = right;
    x2 = left - 1;
}

Результатом этого (на мой взгляд, неоптимального) преобразования является то, что линия

canvas.drawLine(150, 150, 149, 160, paint);

точно параллельно

canvas.drawLine(150, 150, 151, 160, paint);

Я думаю, что все ожидают своего рода V, поскольку конечные точки разделены как минимум на 1 пиксель (их расстояние равно двум пикселям), а начальные точки идентичны.

Но при тестировании на различных устройствах и версиях Android 1-я идеально вертикальная линия находится в столбце 149 пикселей, а 2-я - в столбце 150.

Кстати: правильное преобразование для использования понятия «начальный и конечный пиксель в»:

int x1, y1, x2, y2;           // x1/y1 and x2/y2 pixels inclusive

if ( x1 <= x2 )
    ++x2;
else
    ++x1;
if ( y1 <= y2 )
    ++y2;
else
    ++y1;
canvas.drawLine(x1, y1, x2, y2, paint);
1 голос
/ 18 февраля 2014

Ваш вопрос по сути является ответом для этого . Спасибо.

Я, например, не удивлен этим, и не хотел бы этого иначе. И вот почему:

  • Это соответствует java.awt, javax.swing и другим API, а также основным Java-методам, таким как String.substring(int, int) и List<?>.get(int).

  • Он совместим с android.graphics.RectF, который не заботится о пикселях - как вы можете получить долю пикселя?

  • API удобно:

    • Для прямоугольника 40 × 30 создать экземпляр Rect(0, 0, 40, 30)

    • Rect.right - Rect.left == Rect.width()

  • Таким образом, математика и геометрия проще. Например, если вы хотите нарисовать прямоугольник с центром в пределах холста:

    Rect clipBounds = canvas.getClipBounds()
    int myRectWidth = 20;
    int myRectHeight = 10;
    int left = (clipBounds.width() - myRectWidth) / 2;
    int top = (clipBounds.height() - myRectHeight) / 2;
    int right = clipBounds.width() - left; // See how nice this is?
    int bottom = clipBounds.height() - top; // This wouldn't work if bottom was inclusive!
    canvas.drawRect(left, top, right, bottom);

API правильный; документация API , к сожалению, просто не хватает деталей.

(На самом деле, это упомянуто в подробности для Rect.contains(int, int) метода . К сожалению, Google выпустил API, однако сами переменные поля не были задокументированы.)

0 голосов
/ 23 января 2012

Ваш прямоугольник должен работать с плавниками, попробуйте добавить немного фона, ваша линия теперь имеет размер пикселя, два последних параметра - это не длина, а конечное положение.

0 голосов
/ 23 января 2012

если ваш фон черный, добавьте одно утверждение paint.setColor(Color.RED); а также у вас был пропуск RectF rect = new RectF(100, 100, 100, 100); все точки одинаковы, попробуйте RectF rect = new RectF(100, 100, 200, 200);

ИЛИ для линии canvas.drawLine(10, 10, 10, 10 + 15, paint);

...