Код рисования PaintCode Android использует пиксели вместо точек/DP

Примечание. Да, я знаю, что в Android есть и другие способы создания кнопок, но это всего лишь пример, демонстрирующий мою проблему (на самом деле кнопки намного сложнее). Поэтому, пожалуйста, не отвечайте, предлагая другие решения для кнопок в Android, я ищу решение с помощью PaintCode...

Я много лет использую PaintCode для рисования пользовательских кнопок в iOS, он отлично работает. Я хочу сделать то же самое для Android и иметь следующую проблему:

  1. В PaintCode я рисую кнопку, которая представляет собой прямоугольник со скругленными углами и радиусом 20 точек.
  2. Я рисую рамку вокруг, а затем устанавливаю правильное поведение при изменении размера с помощью пружин (см. Скриншот).
  3. В результате, каким бы ни был размер кнопки (= рамка), углы всегда будут красиво округлены с 20 точками. По сути, это кнопка с изменяемым размером.

Это очень хорошо работает на iOS, но на Android радиус составляет 20 пикселей, а не точек, что приводит к очень маленькому радиусу (теперь с устройствами с высоким разрешением).

Или вообще все рисунки, которые я делаю в PaintCode, когда я рисую с помощью метода рисования, сгенерированного PaintCode, слишком малы.

Похоже, сгенерированный код отрисовки не учитывает масштаб устройства (как в iOS).

введите здесь описание изображения

Глядя на https://www.paintcodeapp.com/documentation/android раздел "масштаб" PaintCode предлагаю поиграть с метрикой плотности в андроиде, чтобы выполнить масштабирование. Это работает, но делает сгенерированный рисунок нечетким, я думаю, это потому, что мы рисуем в более низком разрешении из-за масштабирования. Так что это не жизнеспособное решение.

class Button1 @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : Button(context, attrs, defStyleAttr) {

    var frame = RectF()

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        val displayDensity = resources.displayMetrics.density
        canvas?.scale(displayDensity, displayDensity)
        frame.set(0f,0f,width.toFloat()/displayDensity,height.toFloat()/displayDensity)
        StyleKitName.drawButton1(canvas, frame)
    }
}

Любые предложения, чтобы решить эту проблему? Это ошибка в PaintCode?


person HixField    schedule 27.09.2017    source источник


Ответы (2)


Я разработчик. Извините за длинный ответ.

TLDR: справьтесь с масштабированием самостоятельно, например, так, как вы это делаете. Переключите тип слоя вашего представления на программное обеспечение, чтобы избежать размытых результатов масштабирования.

Во-первых, я полностью понимаю путаницу, для iOS это просто работает, а в Android нужно возиться с некоторыми весами. Было бы гораздо больше смысла, если бы он работал так же, как мне, а также другим пользователям PaintCode. Но это не ошибка. Проблема в разнице между UIKit и android.graphic.

В UIKit расстояния измеряются в пунктах. Это означает, что если вы рисуете круг диаметром 40 точек, он должен быть более-менее одинакового размера на разных устройствах iOS. PaintCode принял это соглашение, и все числа, которые вы видите в пользовательском интерфейсе PaintCode, такие как положение фигур, ширина штриха или радиус — все в точках. Код рисования, сгенерированный PaintCode, не зависит не только от разрешения (т. е. вы можете изменять его размер/масштаб, и он сохраняет резкость), но также не зависит от плотности экрана (отрисовывается примерно одинакового размера на дисплее Retina, обычном дисплее и дисплее Retina HD). И в коде нет ничего особенного. Это выглядит так:

NSBezierPath* rectanglePath = [NSBezierPath bezierPathWithRect: NSMakeRect(0, 0, 100, 50)];
[NSColor.grayColor setFill];
[rectanglePath fill];

Таким образом, масштабирование дисплея обрабатывается UIKit. Также неявный масштаб зависит от контекста. Если вы вызываете код рисования в drawRect: какого-либо подкласса UIView, он принимает плотность отображения, но если вы рисуете внутри пользовательского UIImage, он принимает плотность этого изображения. Магия.

Затем мы добавили поддержку Android. Все показатели в android.graphic представлены в пикселях. Android не использует магию «плотности отображения» UIKit. Также нет хорошего способа узнать, какова плотность в рамках кода отрисовки. Для этого вам нужен доступ к ресурсам. Таким образом, мы могли бы добавить это как параметр ко всем методам рисования. Но что, если вы не собираетесь публиковать рисунок на дисплее, а скорее создаете изображение (которое вы собираетесь отправить своему другу или кому-то еще)? Тогда вам нужна не плотность отображения, а плотность изображения. Итак, при добавлении параметра мы должны добавлять не ресурсы, а саму плотность как число с плавающей запятой и генерировать масштабирование внутри каждого метода рисования. А что, если вас не волнует плотность? Что, если все, о чем вы заботитесь, это то, чтобы ваш рисунок заполнил некоторый прямоугольник и имел наилучшее возможное разрешение? На самом деле я думаю, что это обычно так. На мой взгляд, наличие такого большого количества различных разрешений и плотностей экрана делает подход «элемент одного физического размера подходящим для всех» довольно незначительным. Так что в большинстве случаев параметр плотности будет лишним. Мы решили оставить решение о том, как следует обрабатывать шкалу, пользователю.

Теперь о нечеткости масштабированного рисунка. Это еще одно различие между UIKit и android.graphics. Все разработчики должны понимать, что CoreGraphics не очень быстр, когда дело доходит до рендеринга больших сцен с несколькими объектами. Если вы программируете приложения, чувствительные к производительности, вам, вероятно, следует рассмотреть возможность использования SpriteKit или Metal. Преимущество этого в том, что вы не ограничены в том, что вы можете делать в CoreGraphics, и вы почти всегда будете получать очень точные результаты. Масштабирование является одним из таких примеров. Вы можете применить огромный масштаб, и результат все равно будет четким. Если вам нужно большее аппаратное ускорение, используйте другой API и сами регулируйте ограничения (например, насколько большие текстуры вы можете разместить на своем графическом процессоре).

Android пошел другим путем. Их android.graphic api может работать в двух режимах — без HW-ускорения (они называют это программным) или с HW-ускорением (они называют это аппаратным). Это все тот же API, но один из аппаратных режимов имеет ряд существенных ограничений. Это включает в себя масштаб, размытие (отсюда и тени), некоторые режимы наложения и многое другое. https://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported

И они решили, что каждое представление будет использовать аппаратный режим по умолчанию, если целевой уровень API> = 14. Вы, конечно, можете отключить его, и волшебным образом ваша масштабируемая кнопка будет красивой и четкой.

Мы упоминаем, что вам необходимо отключить аппаратное ускорение в разделе нашей страницы документации «Тип слоя» https://www.paintcodeapp.com/documentation/android И это также есть в документации по Android https://developer.android.com/guide/topics/graphics/hardware-accel.html#controlling

person mato    schedule 27.09.2017
comment
изменение слоя на программное обеспечение помогло (вместе с уже реализованным масштабированием, которое я указал в своих первоначальных вопросах). Большое спасибо за быстрый ответ и отличную поддержку! - person HixField; 28.09.2017
comment
Не могли бы вы взглянуть на этот вопрос? .com/questions/52069634/ - person Kaustubh Bhagwat; 02.09.2018

Я помню, что у меня была похожая проблема, и после разговора с ребятами из службы поддержки PaintCode мы придумали функцию для преобразования DP в PX. Итак, скажем, в Android эти 20 точек будут преобразованы в «любые» пиксели — учитывая разную плотность отображения.

Короче говоря, это краткое изложение ответа службы поддержки PaintCode:

Тот факт, что Android не поддерживает независимые от устройства пиксели в своем API рисования, является причиной различного поведения iOS и Android. При использовании UIKit вводимые координаты указываются в точках, поэтому он сам заботится о плотности отображения. При создании растрового изображения в Android необходимо учитывать плотность отображения.

Это функция, которую я создал:

private static PointF convertDPtoPX(float widthDP, float heightDP) {
    Context context = getAppContext();
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    float widthPX = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, widthDP, metrics);
    float heightPX = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, heightDP, metrics);

    return new PointF(widthPX, heightPX);
}

Вот пример того, как это назвать:

Bitmap myButton = StyleKit.imageOfMyButton(convertDPtoPX(widthDP, heightDP));

Конечно, в этом примере я использую растровое изображение myButton (StyleKit.imageOfMyButton) из-за специфики проекта. Вам нужно будет настроить пример в соответствии с вашими потребностями.

Вы можете, например, сделать этот radius внешним параметром в PaintCode, чтобы каждая платформа отвечала за предоставление своего значения. Нравиться:

// In Android, convert 20DP to 20PX based on the above function.
// In iOS, just pass 20. 
StyleKitName.drawButton1(canvas, frame, radius)

Надеюсь, это поможет.

person backslash-f    schedule 27.09.2017
comment
я столкнулся с той же проблемой, вот как я использую эту функцию Bitmap b = WJArtKit.imageOfIconInterviewUnfixedEmployerRequest (convertDPtoPX (0.2f, 0.2f)); . Но изображение все равно нечеткое, что делать? - person Kaustubh Bhagwat; 28.08.2018
comment
Не могли бы вы взглянуть на этот вопрос? .com/questions/52069634/ - person Kaustubh Bhagwat; 02.09.2018