This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.
Custom drawing (onPaint)
When no widget fits, create a Component and override onPaint(Paint paint). The
coordinates are local (0,0 = top-left corner; width/height are the component’s
size).
Complete example (copy, paste and run)
package com.exemplo;
import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.graphics.*; // Paint, GRect, Font
import dev.crossfly.ui.style.*; // Styles
/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
/** Custom component: draws a "badge" (colored pill with a number). */
static class Badge extends Component
{
private int valor;
Badge(int valor) { this.valor = valor; }
@Override
public void onPaint(Paint paint) throws Exception
{
// LOCAL coordinates: (0,0) = top-left; width/height = component size.
GRect r = new GRect()
.bounds(0, 0, width, height)
.fill(0xFFE53935) // red ARGB
.radius(height / 2); // pill / circle
paint.rect(r);
Font f = Styles.font;
String s = String.valueOf(valor);
int tx = (width - (int) f.width(s)) / 2;
int ty = (height - f.scaledHeight()) / 2;
paint.text(s, tx, ty, 0xFFFFFFFF, f); // centered white text
}
}
@Override
public void onShow() throws Exception
{
super.onShow();
setLayout(new VerticalLayout());
Badge badge = new Badge(7);
add(LAY_AUTO, badge); // adds the custom component to the screen
}
}
The snippets below show only the core (what goes inside
onShow()). To run them, place them in theMainWindowskeleton above.
Skeleton
public class Badge extends Component {
private int valor;
public Badge(int valor) { this.valor = valor; }
@Override public void onPaint(Paint paint) throws Exception {
// filled round background
GRect r = new GRect()
.bounds(0, 0, width, height)
.fill(0xFFE53935) // red
.radius(height / 2); // pill/circle
paint.rect(r);
// centered text
Font f = Styles.font;
String s = String.valueOf(valor);
int tx = (width - (int) f.width(s)) / 2;
int ty = (height - f.scaledHeight()) / 2;
paint.text(s, tx, ty, 0xFFFFFFFF, f);
}
}
// to redraw after changing 'value': repaint();
Paint primitives
| Call | Draws |
|---|---|
paint.rect(GRect r) |
rectangle (filled/border/corners/shadow) — configure the GRect |
paint.text(String, x, y, color, Font) |
text |
paint.line(x1, y1, x2, y2, color, thick) |
line |
paint.image(Image, x, y[, w, h]) |
image |
paint.polyline(GLines) / paint.polygon(GLines) |
polyline / polygon |
GRect — the configurable rectangle
Fluent: .bounds(x,y,w,h) · .fill(color) · .border(color, thick) · .radius(r) /
.radius(tl,tr,br,bl). Then paint.rect(gr).
GRect card = new GRect().bounds(0, 0, width, height)
.fill(0xFFFFFFFF).border(0xFFE0E0E0, 1).radius(8);
paint.rect(card);
Best practices
- Draw relative to
width/height(not fixed values) — keeps it responsive. - Reuse
GRect/Fontobjects (fields) instead of allocating per frame. - Call
repaint()when the state that affects the drawing changes (rendering is on demand).
Common mistakes
- Using absolute screen coordinates in
onPaint— they are local to the component. - Forgetting
repaint()after changing the drawn data. - Passing color as an object — colors are
intARGB (theColorclass does not exist).
See also
- Images · Whiteboard · Colors
Was this page helpful?Thanks for the feedback!